package com.ylx.order.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ylx.common.core.domain.model.LoginUser;
import com.ylx.common.exception.ServiceException;
import com.ylx.common.utils.DistanceUtil;
import com.ylx.massage.domain.CancelOrderApplication;
import com.ylx.order.domain.OrderDispatch;
import com.ylx.order.domain.dto.AdminOrderDispatchDTO;
import com.ylx.order.domain.dto.AdminOrderDispatchMerchantQueryDTO;
import com.ylx.order.domain.dto.AdminOrderQueryDTO;
import com.ylx.order.domain.vo.AdminOrderDetailVO;
import com.ylx.order.domain.vo.AdminOrderDispatchMerchantVO;
import com.ylx.order.domain.vo.AdminOrderDispatchOrderVO;
import com.ylx.order.domain.vo.AdminOrderPageVO;
import com.ylx.order.domain.vo.AdminOrderServiceCategoryVO;
import com.ylx.order.enums.AfterSaleServiceStatusEnum;
import com.ylx.order.mapper.AdminOrderMapper;
import com.ylx.order.service.AdminOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AdminOrderServiceImpl implements AdminOrderService {

    private static final String PLATFORM_REFUND_REASON = "平台发起退款申请";

    private static final String USER_SEARCH_PHONE = "phone";
    private static final String USER_SEARCH_NICK_NAME = "nickName";
    private static final String USER_SEARCH_NAME = "name";
    private static final String MERCHANT_SEARCH_NICK_NAME = "nickName";
    private static final String MERCHANT_SEARCH_PHONE = "phone";
    private static final BigDecimal DEFAULT_DISPATCH_RADIUS_METERS = new BigDecimal("10000");
    private static final Integer ORDER_STATUS_PENDING_DISPATCH = 1;
    private static final Integer ORDER_STATUS_PENDING_ACCEPT = 2;
    private static final Integer DISPATCH_STATUS_PENDING_ACCEPT = 1;
    private static final Integer DISPATCH_SOURCE_ADMIN = 1;
    private static final Integer CURRENT_DISPATCH = 1;
    private static final Integer NOT_DELETED = 0;
    private static final BigDecimal MAX_LATITUDE = new BigDecimal("90");
    private static final BigDecimal MAX_LONGITUDE = new BigDecimal("180");

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Resource
    private AdminOrderMapper adminOrderMapper;

    @Override
    public Page<AdminOrderPageVO> page(Page<AdminOrderPageVO> page, AdminOrderQueryDTO dto) {
        AdminOrderQueryDTO query = dto == null ? new AdminOrderQueryDTO() : dto;
        normalizeAndValidate(query);

        Page<AdminOrderPageVO> pageParam = page == null ? new Page<>(1, 10) : page;
        Page<AdminOrderPageVO> result = adminOrderMapper.selectAdminOrderPage(pageParam, query);
        result.getRecords().forEach(this::fillDisplayFields);
        return result;
    }

    @Override
    public Page<AdminOrderPageVO> recyclePage(Page<AdminOrderPageVO> page, AdminOrderQueryDTO dto) {
        AdminOrderQueryDTO query = dto == null ? new AdminOrderQueryDTO() : dto;
        normalizeAndValidate(query);

        Page<AdminOrderPageVO> pageParam = page == null ? new Page<>(1, 10) : page;
        Page<AdminOrderPageVO> result = adminOrderMapper.selectAdminOrderRecyclePage(pageParam, query);
        result.getRecords().forEach(this::fillDisplayFields);
        return result;
    }

    @Override
    public List<AdminOrderServiceCategoryVO> listServiceCategoryOptions() {
        return adminOrderMapper.selectServiceCategoryOptions();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Long id) {
        if (id == null) {
            throw new ServiceException("订单ID不能为空");
        }
        if (id <= 0) {
            throw new ServiceException("订单ID不正确");
        }

        int rows = adminOrderMapper.logicDeleteById(id);
        if (rows <= 0) {
            throw new ServiceException("订单不存在或已删除");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restoreById(Long id) {
        validateOrderId(id);

        int rows = adminOrderMapper.restoreById(id);
        if (rows <= 0) {
            throw new ServiceException("订单不存在或未删除");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyRefund(Long id) {
        validateOrderId(id);

        CancelOrderApplication application = adminOrderMapper.selectRefundApplicationOrderById(id);
        if (application == null) {
            throw new ServiceException("订单不存在或已删除");
        }
        if (adminOrderMapper.countPendingRefundApplicationByOrderNo(application.getOrderNo()) > 0) {
            throw new ServiceException("该订单已存在待审核退款申请");
        }

        LocalDateTime now = LocalDateTime.now();
        application.setId(UUID.randomUUID().toString().replace("-", ""));
        application.setCancelOrderReason(PLATFORM_REFUND_REASON);
        application.setAuditStatus(0);
        application.setApplicationTime(now);
        application.setCreateTime(now);
        application.setUpdateTime(now);
        application.setIsDelete(0);

        int rows = adminOrderMapper.insertRefundApplication(application);
        if (rows <= 0) {
            throw new ServiceException("发起退款申请失败");
        }
    }

    @Override
    public AdminOrderDetailVO detail(Long id) {
        validateOrderId(id);

        // 查询订单详情
        AdminOrderDetailVO.OrderInfoVO orderInfo = adminOrderMapper.selectOrderDetailInfoById(id);
        if (orderInfo == null) {
            throw new ServiceException("订单不存在或已删除");
        }

        fillOrderDetailDisplayFields(orderInfo);
        // 查询项目详情
        AdminOrderDetailVO.ProjectInfoVO projectInfo = adminOrderMapper.selectOrderProjectDetailById(id);
        if (projectInfo != null) {
            fillProjectDetailDisplayFields(projectInfo);
        }

        // 查询订单操作日志
        List<AdminOrderDetailVO.OperationLogVO> operationLogs = adminOrderMapper.selectOrderOperationLogs(id);
        if (operationLogs == null) {
            operationLogs = Collections.emptyList();
        }
        operationLogs.forEach(this::fillOperationLogDisplayFields);

        AdminOrderDetailVO detail = new AdminOrderDetailVO();
        detail.setOrderInfo(orderInfo);
        detail.setProjectInfo(projectInfo);
        detail.setServiceVoucherList(splitStartPhoto(orderInfo.getStartPhoto()));
        detail.setOperationLogList(operationLogs);
        return detail;
    }

    @Override
    public List<AdminOrderDispatchMerchantVO> listDispatchMerchants(Long id, AdminOrderDispatchMerchantQueryDTO dto) {
        AdminOrderDispatchOrderVO order = getDispatchOrder(id);
        validatePendingDispatchOrder(order);
        validateDispatchOrderLocation(order);

        AdminOrderDispatchMerchantQueryDTO query = normalizeDispatchMerchantQuery(dto);
        return adminOrderMapper.selectDispatchMerchantCandidates(order, query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void dispatch(Long id, AdminOrderDispatchDTO dto) {
        if (dto == null || dto.getMerchantId() == null) {
            throw new ServiceException("请选择商户");
        }
        if (dto.getMerchantId() <= 0) {
            throw new ServiceException("商户ID不正确");
        }

        AdminOrderDispatchOrderVO order = getDispatchOrder(id);
        validatePendingDispatchOrder(order);
        validateDispatchOrderLocation(order);

        // 处理派单半径，默认10000米
        BigDecimal radiusMeters = normalizeDispatchRadius(dto.getRadiusMeters());
        AdminOrderDispatchMerchantVO merchant = adminOrderMapper.selectDispatchMerchantById(order, dto.getMerchantId(), radiusMeters);
        if (merchant == null) {
            throw new ServiceException("请选择可派单商户");
        }

        if (adminOrderMapper.countCurrentDispatchByOrderId(id) > 0) {
            throw new ServiceException("该订单已存在有效派单记录，请刷新重试");
        }

        int orderRows = adminOrderMapper.updateOrderDispatch(id, merchant, ORDER_STATUS_PENDING_DISPATCH, ORDER_STATUS_PENDING_ACCEPT);
        if (orderRows <= 0) {
            throw new ServiceException("操作失败，请刷新重试");
        }

        OrderDispatch dispatch = buildOrderDispatch(order, merchant, dto);
        int dispatchRows = adminOrderMapper.insertOrderDispatch(dispatch);
        if (dispatchRows <= 0) {
            throw new ServiceException("保存派单记录失败");
        }

        int flowRows = adminOrderMapper.insertOrderStatusFlow(id, ORDER_STATUS_PENDING_ACCEPT, getCurrentOperator());
        if (flowRows <= 0) {
            throw new ServiceException("保存订单状态流转失败");
        }
    }

    /**
     * 获取待派单订单详情
     * @param id
     * @return AdminOrderDispatchOrderVO
     */
    private AdminOrderDispatchOrderVO getDispatchOrder(Long id) {
        validateOrderId(id);
        AdminOrderDispatchOrderVO order = adminOrderMapper.selectDispatchOrderById(id);
        if (order == null) {
            throw new ServiceException("订单不存在或已删除");
        }
        return order;
    }

    /**
     * 验证待派单订单的状态
     *
     * @param order
     */
    private void validatePendingDispatchOrder(AdminOrderDispatchOrderVO order) {
        if (!ORDER_STATUS_PENDING_DISPATCH.equals(order.getStatus())) {
            throw new ServiceException("当前订单状态不是待派单状态，请刷新重试");
        }
    }

    /**
     * 验证待派单订单的下单坐标是否为空
     *
     * @param order
     */
    private void validateDispatchOrderLocation(AdminOrderDispatchOrderVO order) {
        if (order.getUserLatitude() == null || order.getUserLongitude() == null) {
            throw new ServiceException("订单下单坐标不能为空");
        }
    }

    /**
     * 构建派单商户查询参数，处理默认值和空值
     *
     * @param dto
     * @return AdminOrderDispatchMerchantQueryDTO
     */
    private AdminOrderDispatchMerchantQueryDTO normalizeDispatchMerchantQuery(AdminOrderDispatchMerchantQueryDTO dto) {
        AdminOrderDispatchMerchantQueryDTO query = dto == null ? new AdminOrderDispatchMerchantQueryDTO() : dto;
        query.setRadiusMeters(normalizeDispatchRadius(query.getRadiusMeters()));
        if (StringUtils.hasText(query.getKeyword())) {
            query.setKeyword(query.getKeyword().trim());
        } else {
            query.setKeyword(null);
        }
        if (StringUtils.hasText(query.getCityCode())) {
            query.setCityCode(query.getCityCode().trim());
        } else {
            query.setCityCode(null);
        }
        return query;
    }

    /**
     * 处理派单半径，默认值为1000米
     *
     * @param radiusMeters
     * @return BigDecimal
     */
    private BigDecimal normalizeDispatchRadius(BigDecimal radiusMeters) {
        if (radiusMeters == null || radiusMeters.compareTo(BigDecimal.ZERO) <= 0) {
            return DEFAULT_DISPATCH_RADIUS_METERS;
        }
        return radiusMeters;
    }

    /**
     * 构建订单派单记录
     * @param order
     * @param merchant
     * @param dto
     * @return OrderDispatch
     */
    private OrderDispatch buildOrderDispatch(AdminOrderDispatchOrderVO order, AdminOrderDispatchMerchantVO merchant, AdminOrderDispatchDTO dto) {
        LocalDateTime now = LocalDateTime.now();
        String operator = getCurrentOperator();
        OrderDispatch dispatch = new OrderDispatch();
        dispatch.setOrderId(order.getOrderId());
        dispatch.setOrderNo(order.getOrderNo());
        dispatch.setMerchantId(merchant.getMerchantId());
        dispatch.setMerchantName(merchant.getMerchantName());
        dispatch.setMerchantNickName(merchant.getMerchantNickName());
        dispatch.setMerchantPhone(merchant.getMerchantPhone());
        dispatch.setMerchantAvatar(merchant.getMerchantAvatar());
        dispatch.setMerchantType(merchant.getMerchantType());
        dispatch.setProjectId(order.getProjectId());
        dispatch.setProjectName(order.getProjectName());
        dispatch.setCategoryId(order.getCategoryId());
        dispatch.setCityCode(StringUtils.hasText(dto.getCityCode()) ? dto.getCityCode().trim() : null);
        dispatch.setUserLatitude(order.getUserLatitude());
        dispatch.setUserLongitude(order.getUserLongitude());
        dispatch.setMerchantLatitude(merchant.getMerchantLatitude());
        dispatch.setMerchantLongitude(merchant.getMerchantLongitude());
        dispatch.setDistanceMeters(merchant.getDistanceMeters());
        dispatch.setDispatchStatus(DISPATCH_STATUS_PENDING_ACCEPT);
        dispatch.setDispatchSource(DISPATCH_SOURCE_ADMIN);
        dispatch.setCurrentFlag(CURRENT_DISPATCH);
        dispatch.setOrderStatusBefore(ORDER_STATUS_PENDING_DISPATCH);
        dispatch.setOrderStatusAfter(ORDER_STATUS_PENDING_ACCEPT);
        dispatch.setDispatchTime(now);
        dispatch.setOperatorName(operator);
        dispatch.setCreateBy(operator);
        dispatch.setCreateTime(now);
        dispatch.setUpdateBy(operator);
        dispatch.setUpdateTime(now);
        dispatch.setIsDelete(NOT_DELETED);
        return dispatch;
    }

    private String getCurrentOperator() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || authentication.getPrincipal() == null) {
            return "系统";
        }
        Object principal = authentication.getPrincipal();
        if (principal instanceof LoginUser) {
            String username = ((LoginUser) principal).getUsername();
            return StringUtils.hasText(username) ? username : "系统";
        }
        if (principal instanceof String && StringUtils.hasText((String) principal)) {
            return (String) principal;
        }
        return "系统";
    }

    /**
     * 填充订单详情显示字段
     *
     * @param orderInfo
     */
    private void fillOrderDetailDisplayFields(AdminOrderDetailVO.OrderInfoVO orderInfo) {
        orderInfo.setStatusName(getStatusName(orderInfo.getStatus()));
        orderInfo.setPaymentMethodName(getPaymentMethodName(orderInfo.getStatus(), orderInfo.getPaymentMethod()));
        orderInfo.setAfterSalesServiceStatusName(getAfterSalesServiceStatusName(orderInfo.getAfterSalesServiceStatus()));

        // 获取用户下单的经纬度
        BigDecimal userLatitude = orderInfo.getUserLatitude();
        BigDecimal userLongitude = orderInfo.getUserLongitude();

        //获取商户下单的真实经纬度
        BigDecimal merchantLatitude = orderInfo.getMerchantLatitude();
        BigDecimal merchantLongitude = orderInfo.getMerchantLongitude();

        //计算距离
        BigDecimal distanceMeters = calculateDistance(userLatitude, userLongitude, merchantLatitude, merchantLongitude);
        log.info("用户与商户距离：{}米", distanceMeters);
        //把米转换为公里
        orderInfo.setDistance(distanceMeters.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP));
    }

    /**
     * 计算用户与商户两点之间的球面直线距离，单位：米。
     *
     * @param userLatitude      用户纬度
     * @param userLongitude     用户经度
     * @param merchantLatitude  商户纬度
     * @param merchantLongitude 商户经度
     * @return 距离，保留2位小数；坐标缺失或非法时返回null
     */
    private BigDecimal calculateDistance(BigDecimal userLatitude, BigDecimal userLongitude, BigDecimal merchantLatitude, BigDecimal merchantLongitude) {
        if (!isCoordinateValid(userLatitude, userLongitude) || !isCoordinateValid(merchantLatitude, merchantLongitude)) {
            return null;
        }
        double distanceMeters = DistanceUtil.getDistance(
                userLatitude.doubleValue(),
                userLongitude.doubleValue(),
                merchantLatitude.doubleValue(),
                merchantLongitude.doubleValue());
        return BigDecimal.valueOf(distanceMeters).setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 校验单组经纬度是否完整且在合法范围内。
     *
     * @param latitude  纬度
     * @param longitude 经度
     * @return true-合法，false-非法
     */
    private boolean isCoordinateValid(BigDecimal latitude, BigDecimal longitude) {
        if (latitude == null || longitude == null) {
            return false;
        }
        if (BigDecimal.ZERO.compareTo(latitude) == 0 || BigDecimal.ZERO.compareTo(longitude) == 0) {
            return false;
        }
        return latitude.abs().compareTo(MAX_LATITUDE) <= 0
                && longitude.abs().compareTo(MAX_LONGITUDE) <= 0;
    }

    /**
     * 填充项目详情显示字段
     *
     * @param projectInfo
     */
    private void fillProjectDetailDisplayFields(AdminOrderDetailVO.ProjectInfoVO projectInfo) {
        projectInfo.setUnitName(getUnitName(projectInfo.getUnitType()));
        //projectInfo.setAfterSalesServiceStatusName(getAfterSalesServiceStatusName(projectInfo.getAfterSalesServiceStatus()));
    }

    /**
     * 填充订单操作日志显示字段
     * @param log
     */
    private void fillOperationLogDisplayFields(AdminOrderDetailVO.OperationLogVO log) {
        String statusName = getStatusName(log.getStatus());
        log.setStatusName(statusName);
        log.setOperationContent(statusName);
    }

    private String getAfterSalesServiceStatusName(Integer status) {
        AfterSaleServiceStatusEnum statusEnum = AfterSaleServiceStatusEnum.valueOf(status);
        return statusEnum == null ? "" : statusEnum.getDesc();
    }

    /**
     * 解析开始服务照片
     *
     * @param startPhoto
     * @return List<String> 开始服务照片列表
     */
    private List<String> splitStartPhoto(String startPhoto) {
        if (!StringUtils.hasText(startPhoto)) {
            return new ArrayList<>();
        }
        return java.util.Arrays.stream(startPhoto.split(","))
                .map(String::trim)
                .filter(StringUtils::hasText)
                .collect(Collectors.toList());
    }

    /**
     * 验证订单ID
     *
     * @param id
     */
    private void validateOrderId(Long id) {
        if (id == null) {
            throw new ServiceException("订单ID不能为空");
        }
        if (id <= 0) {
            throw new ServiceException("订单ID不正确");
        }
    }

    /**
     * 校验并格式化查询参数
     * @param dto
     */
    private void normalizeAndValidate(AdminOrderQueryDTO dto) {
        dto.setUserSearchType(normalizeUserSearchType(dto.getUserSearchType()));
        dto.setMerchantSearchType(normalizeMerchantSearchType(dto.getMerchantSearchType()));

        Date startTime = parseQueryTime(dto.getStartTime(), true);
        Date endTime = parseQueryTime(dto.getEndTime(), false);
        if (startTime != null && endTime != null && startTime.after(endTime)) {
            throw new ServiceException("开始时间不能大于结束时间");
        }
        dto.setQueryStartTime(startTime);
        dto.setQueryEndTime(endTime);
    }

    private String normalizeUserSearchType(String searchType) {
        if (!StringUtils.hasText(searchType)) {
            return USER_SEARCH_PHONE;
        }
        if (USER_SEARCH_PHONE.equals(searchType) || USER_SEARCH_NICK_NAME.equals(searchType)
                || USER_SEARCH_NAME.equals(searchType)) {
            return searchType;
        }
        throw new ServiceException("用户搜索类型不正确");
    }

    private String normalizeMerchantSearchType(String searchType) {
        if (!StringUtils.hasText(searchType)) {
            return MERCHANT_SEARCH_NICK_NAME;
        }
        if (MERCHANT_SEARCH_NICK_NAME.equals(searchType) || MERCHANT_SEARCH_PHONE.equals(searchType)) {
            return searchType;
        }
        throw new ServiceException("商户搜索类型不正确");
    }

    private Date parseQueryTime(String value, boolean startOfDay) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        String text = value.trim();
        try {
            LocalDateTime dateTime;
            if (text.length() == 10) {
                LocalDate date = LocalDate.parse(text, DATE_FORMATTER);
                dateTime = startOfDay ? date.atStartOfDay() : date.atTime(LocalTime.MAX);
            } else {
                dateTime = LocalDateTime.parse(text, DATE_TIME_FORMATTER);
            }
            return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
        } catch (DateTimeParseException e) {
            throw new ServiceException("下单时间格式错误，请使用 yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss");
        }
    }

    /**
     * 填充订单显示字段
     * @param vo
     */
    private void fillDisplayFields(AdminOrderPageVO vo) {
        vo.setContactPhoneMasked(maskPhone(vo.getContactPhoneNumber()));
        vo.setStatusName(getStatusName(vo.getStatus()));
        vo.setExecStatusName(getExecStatusName(vo.getExecStatus()));
        vo.setPaymentMethodName(getPaymentMethodName(vo.getStatus(), vo.getPaymentMethod()));
        vo.setAmountTypeName(Integer.valueOf(0).equals(vo.getStatus()) ? "应付" : "实付");
        vo.setUnitName(getUnitName(vo.getUnitType()));
        if (vo.getQuantity() == null) {
            vo.setQuantity(1);
        }
    }

    private String maskPhone(String phone) {
        if (!StringUtils.hasText(phone) || phone.length() < 7) {
            return phone;
        }
        return phone.substring(0, 3) + "****" + phone.substring(7);
    }

    private String getPaymentMethodName(Integer status, Integer paymentMethod) {
        if (Integer.valueOf(0).equals(status)) {
            return "--";
        }
        if (Integer.valueOf(0).equals(paymentMethod)) {
            return "购物卡支付";
        }
        if (Integer.valueOf(1).equals(paymentMethod)) {
            return "微信支付";
        }
        return "";
    }

    private String getStatusName(Integer status) {
        if (status == null) {
            return "";
        }
        switch (status) {
            case 0:
                return "待支付";
            case 1:
                return "待派单";
            case 2:
                return "待接单";
            case 3:
                return "待服务";
            case 4:
                return "服务中";
            case 5:
                return "已完成";
            case 6:
                return "已取消";
            case 7:
                return "已关闭";
            case 8:
                return "拒绝接单";
            case 9:
                return "退款成功";
            default:
                return "未知";
        }
    }

    private String getExecStatusName(Integer execStatus) {
        if (execStatus == null) {
            return "";
        }
        switch (execStatus) {
            case 0:
                return "待出发/待签到";
            case 1:
                return "已出发";
            case 2:
                return "已到达/已签到";
            default:
                return "未知";
        }
    }

    private String getUnitName(Integer unitType) {
        if (unitType == null) {
            return "";
        }
        switch (unitType) {
            case 0:
                return "分钟";
            case 1:
                return "小时";
            case 2:
                return "次";
            default:
                return String.valueOf(unitType);
        }
    }
}
