package com.ylx.order.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ylx.common.exception.ServiceException;
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.AdminOrderDispatchMerchantVO;
import com.ylx.order.domain.vo.AdminOrderDispatchOrderVO;
import com.ylx.order.domain.vo.AdminOrderDetailVO;
import com.ylx.order.domain.vo.AdminOrderPageVO;
import com.ylx.order.mapper.AdminOrderMapper;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;

import java.math.BigDecimal;
import java.util.Collections;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class AdminOrderServiceImplTest {

    @Test
    public void adminOrderPageRejectsStartTimeAfterEndTime() {
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        AdminOrderQueryDTO dto = new AdminOrderQueryDTO();
        dto.setStartTime("2026-06-12 00:00:00");
        dto.setEndTime("2026-06-11 00:00:00");

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.page(new Page<AdminOrderPageVO>(), dto));

        assertEquals("开始时间不能大于结束时间", exception.getMessage());
    }

    @Test
    public void deleteByIdRejectsEmptyId() {
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.deleteById(null));

        assertEquals("订单ID不能为空", exception.getMessage());
    }

    @Test
    public void recyclePageUsesRecycleMapperAndFillsDisplayFields() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        Page<AdminOrderPageVO> mapperResult = new Page<>();
        AdminOrderPageVO record = new AdminOrderPageVO();
        record.setContactPhoneNumber("13812345678");
        record.setQuantity(null);
        mapperResult.setRecords(Collections.singletonList(record));
        when(mapper.selectAdminOrderRecyclePage(org.mockito.ArgumentMatchers.any(Page.class),
                org.mockito.ArgumentMatchers.any(AdminOrderQueryDTO.class))).thenReturn(mapperResult);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);
        AdminOrderQueryDTO dto = new AdminOrderQueryDTO();
        dto.setStartTime("2026-06-01");

        Page<AdminOrderPageVO> result = service.recyclePage(null, dto);

        ArgumentCaptor<AdminOrderQueryDTO> dtoCaptor = ArgumentCaptor.forClass(AdminOrderQueryDTO.class);
        verify(mapper).selectAdminOrderRecyclePage(org.mockito.ArgumentMatchers.any(Page.class), dtoCaptor.capture());
        assertNotNull(dtoCaptor.getValue().getQueryStartTime());
        assertEquals(mapperResult, result);
        assertEquals("138****5678", result.getRecords().get(0).getContactPhoneMasked());
        assertEquals(1, result.getRecords().get(0).getQuantity());
    }

    @Test
    public void deleteByIdRejectsInvalidId() {
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.deleteById(0L));

        assertEquals("订单ID不正确", exception.getMessage());
    }

    @Test
    public void deleteByIdThrowsWhenOrderMissingOrDeleted() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        when(mapper.logicDeleteById(1L)).thenReturn(0);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.deleteById(1L));

        assertEquals("订单不存在或已删除", exception.getMessage());
    }

    @Test
    public void deleteByIdLogicDeletesExistingOrder() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        when(mapper.logicDeleteById(1L)).thenReturn(1);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        service.deleteById(1L);

        verify(mapper).logicDeleteById(1L);
    }

    @Test
    public void restoreByIdThrowsWhenOrderMissingOrNotDeleted() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        when(mapper.restoreById(1L)).thenReturn(0);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.restoreById(1L));

        assertEquals("订单不存在或未删除", exception.getMessage());
    }

    @Test
    public void restoreByIdRestoresDeletedOrder() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        when(mapper.restoreById(1L)).thenReturn(1);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        service.restoreById(1L);

        verify(mapper).restoreById(1L);
    }

    @Test
    public void applyRefundRejectsEmptyId() {
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.applyRefund(null));

        assertEquals("订单ID不能为空", exception.getMessage());
    }

    @Test
    public void applyRefundRejectsMissingOrder() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        when(mapper.selectRefundApplicationOrderById(1L)).thenReturn(null);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.applyRefund(1L));

        assertEquals("订单不存在或已删除", exception.getMessage());
    }

    @Test
    public void applyRefundRejectsPendingApplication() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        CancelOrderApplication application = buildRefundApplicationOrder();
        when(mapper.selectRefundApplicationOrderById(1L)).thenReturn(application);
        when(mapper.countPendingRefundApplicationByOrderNo("NO001")).thenReturn(1);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.applyRefund(1L));

        assertEquals("该订单已存在待审核退款申请", exception.getMessage());
    }

    @Test
    public void applyRefundCreatesApplicationWithDefaultReason() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        CancelOrderApplication application = buildRefundApplicationOrder();
        when(mapper.selectRefundApplicationOrderById(1L)).thenReturn(application);
        when(mapper.countPendingRefundApplicationByOrderNo("NO001")).thenReturn(0);
        when(mapper.insertRefundApplication(org.mockito.ArgumentMatchers.any(CancelOrderApplication.class))).thenReturn(1);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        service.applyRefund(1L);

        ArgumentCaptor<CancelOrderApplication> captor = ArgumentCaptor.forClass(CancelOrderApplication.class);
        verify(mapper).insertRefundApplication(captor.capture());
        CancelOrderApplication saved = captor.getValue();
        assertNotNull(saved.getId());
        assertEquals("NO001", saved.getOrderNo());
        assertEquals("平台发起退款申请", saved.getCancelOrderReason());
        assertEquals(0, saved.getAuditStatus());
        assertEquals(0, saved.getIsDelete());
        assertEquals(new BigDecimal("100.00"), saved.getRefundAmount());
        assertNotNull(saved.getApplicationTime());
        assertNotNull(saved.getCreateTime());
        assertNotNull(saved.getUpdateTime());
    }

    @Test
    public void detailRejectsMissingOrder() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        when(mapper.selectOrderDetailInfoById(1L)).thenReturn(null);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.detail(1L));

        assertEquals("订单不存在或已删除", exception.getMessage());
    }

    @Test
    public void detailBuildsVoucherListAndOperationContent() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDetailVO.OrderInfoVO orderInfo = new AdminOrderDetailVO.OrderInfoVO();
        orderInfo.setStatus(4);
        orderInfo.setPaymentMethod(1);
        orderInfo.setAfterSalesServiceStatus(3);
        orderInfo.setStartPhoto("a.jpg,b.jpg");
        AdminOrderDetailVO.ProjectInfoVO projectInfo = new AdminOrderDetailVO.ProjectInfoVO();
        projectInfo.setMerchantCommission(new BigDecimal("80.00"));
        AdminOrderDetailVO.OperationLogVO log = new AdminOrderDetailVO.OperationLogVO();
        log.setStatus(4);

        when(mapper.selectOrderDetailInfoById(1L)).thenReturn(orderInfo);
        when(mapper.selectOrderProjectDetailById(1L)).thenReturn(projectInfo);
        when(mapper.selectOrderOperationLogs(1L)).thenReturn(Collections.singletonList(log));
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        AdminOrderDetailVO detail = service.detail(1L);

        assertEquals("服务中", detail.getOrderInfo().getStatusName());
        assertEquals("微信支付", detail.getOrderInfo().getPaymentMethodName());
        assertEquals("退款成功", detail.getOrderInfo().getAfterSalesServiceStatusName());
        assertEquals(2, detail.getServiceVoucherList().size());
        assertEquals("a.jpg", detail.getServiceVoucherList().get(0));
        assertEquals(new BigDecimal("80.00"), detail.getProjectInfo().getMerchantCommission());
        assertEquals("服务中", detail.getOperationLogList().get(0).getOperationContent());
        assertEquals("服务中", detail.getOperationLogList().get(0).getStatusName());
    }

    @Test
    public void detailCalculatesDistanceBetweenUserAndMerchantCoordinates() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDetailVO.OrderInfoVO orderInfo = new AdminOrderDetailVO.OrderInfoVO();
        orderInfo.setUserLatitude(new BigDecimal("30.000000"));
        orderInfo.setUserLongitude(new BigDecimal("120.000000"));
        orderInfo.setMerchantLatitude(new BigDecimal("30.010000"));
        orderInfo.setMerchantLongitude(new BigDecimal("120.010000"));

        when(mapper.selectOrderDetailInfoById(1L)).thenReturn(orderInfo);
        when(mapper.selectOrderProjectDetailById(1L)).thenReturn(null);
        when(mapper.selectOrderOperationLogs(1L)).thenReturn(Collections.emptyList());
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        AdminOrderDetailVO detail = service.detail(1L);

        BigDecimal distance = detail.getOrderInfo().getDistance();
        assertNotNull(distance);
        assertTrue(distance.compareTo(new BigDecimal("1460")) > 0);
        assertTrue(distance.compareTo(new BigDecimal("1480")) < 0);
        assertEquals(2, distance.scale());
    }

    @Test
    public void detailLeavesDistanceEmptyWhenCoordinatesAreMissing() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDetailVO.OrderInfoVO orderInfo = new AdminOrderDetailVO.OrderInfoVO();
        orderInfo.setUserLatitude(new BigDecimal("30.000000"));
        orderInfo.setUserLongitude(new BigDecimal("120.000000"));

        when(mapper.selectOrderDetailInfoById(1L)).thenReturn(orderInfo);
        when(mapper.selectOrderProjectDetailById(1L)).thenReturn(null);
        when(mapper.selectOrderOperationLogs(1L)).thenReturn(Collections.emptyList());
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        AdminOrderDetailVO detail = service.detail(1L);

        assertEquals(null, detail.getOrderInfo().getDistance());
    }

    @Test
    public void detailLeavesDistanceEmptyWhenCoordinatesAreZero() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDetailVO.OrderInfoVO orderInfo = new AdminOrderDetailVO.OrderInfoVO();
        orderInfo.setUserLatitude(BigDecimal.ZERO);
        orderInfo.setUserLongitude(new BigDecimal("120.000000"));
        orderInfo.setMerchantLatitude(new BigDecimal("30.010000"));
        orderInfo.setMerchantLongitude(new BigDecimal("120.010000"));

        when(mapper.selectOrderDetailInfoById(1L)).thenReturn(orderInfo);
        when(mapper.selectOrderProjectDetailById(1L)).thenReturn(null);
        when(mapper.selectOrderOperationLogs(1L)).thenReturn(Collections.emptyList());
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        AdminOrderDetailVO detail = service.detail(1L);

        assertEquals(null, detail.getOrderInfo().getDistance());
    }

    @Test
    public void dispatchMerchantListRejectsOrderNotPendingDispatch() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDispatchOrderVO order = buildDispatchOrder();
        order.setStatus(2);
        when(mapper.selectDispatchOrderById(1L)).thenReturn(order);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.listDispatchMerchants(1L, new AdminOrderDispatchMerchantQueryDTO()));

        assertEquals("操作失败，请刷新重试", exception.getMessage());
    }

    @Test
    public void dispatchMerchantListUsesDefaultRadiusAndTrimmedKeyword() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDispatchOrderVO order = buildDispatchOrder();
        when(mapper.selectDispatchOrderById(1L)).thenReturn(order);
        when(mapper.selectDispatchMerchantCandidates(
                org.mockito.ArgumentMatchers.any(AdminOrderDispatchOrderVO.class),
                org.mockito.ArgumentMatchers.any(AdminOrderDispatchMerchantQueryDTO.class)))
                .thenReturn(Collections.emptyList());
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);
        AdminOrderDispatchMerchantQueryDTO query = new AdminOrderDispatchMerchantQueryDTO();
        query.setKeyword(" NaNa ");

        service.listDispatchMerchants(1L, query);

        ArgumentCaptor<AdminOrderDispatchMerchantQueryDTO> queryCaptor =
                ArgumentCaptor.forClass(AdminOrderDispatchMerchantQueryDTO.class);
        verify(mapper).selectDispatchMerchantCandidates(
                org.mockito.ArgumentMatchers.eq(order), queryCaptor.capture());
        assertEquals("NaNa", queryCaptor.getValue().getKeyword());
        assertEquals(new BigDecimal("10000"), queryCaptor.getValue().getRadiusMeters());
    }

    @Test
    public void dispatchOrderRejectsEmptyMerchant() {
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        AdminOrderDispatchDTO dto = new AdminOrderDispatchDTO();

        ServiceException exception = assertThrows(ServiceException.class,
                () -> service.dispatch(1L, dto));

        assertEquals("请选择商户", exception.getMessage());
    }

    @Test
    public void dispatchOrderUpdatesOrderInsertsDispatchAndStatusFlow() {
        AdminOrderMapper mapper = mock(AdminOrderMapper.class);
        AdminOrderDispatchOrderVO order = buildDispatchOrder();
        AdminOrderDispatchMerchantVO merchant = buildDispatchMerchant();
        when(mapper.selectDispatchOrderById(1L)).thenReturn(order);
        when(mapper.selectDispatchMerchantById(
                org.mockito.ArgumentMatchers.any(AdminOrderDispatchOrderVO.class),
                org.mockito.ArgumentMatchers.eq(10),
                org.mockito.ArgumentMatchers.any(BigDecimal.class))).thenReturn(merchant);
        when(mapper.countCurrentDispatchByOrderId(1L)).thenReturn(0);
        when(mapper.updateOrderDispatch(
                org.mockito.ArgumentMatchers.eq(1L),
                org.mockito.ArgumentMatchers.eq(merchant),
                org.mockito.ArgumentMatchers.eq(1),
                org.mockito.ArgumentMatchers.eq(2))).thenReturn(1);
        when(mapper.insertOrderDispatch(org.mockito.ArgumentMatchers.any(OrderDispatch.class))).thenReturn(1);
        when(mapper.insertOrderStatusFlow(
                org.mockito.ArgumentMatchers.eq(1L),
                org.mockito.ArgumentMatchers.eq(2),
                org.mockito.ArgumentMatchers.anyString())).thenReturn(1);
        AdminOrderServiceImpl service = new AdminOrderServiceImpl();
        ReflectionTestUtils.setField(service, "adminOrderMapper", mapper);
        AdminOrderDispatchDTO dto = new AdminOrderDispatchDTO();
        dto.setMerchantId(10);

        service.dispatch(1L, dto);

        ArgumentCaptor<OrderDispatch> dispatchCaptor = ArgumentCaptor.forClass(OrderDispatch.class);
        verify(mapper).insertOrderDispatch(dispatchCaptor.capture());
        OrderDispatch dispatch = dispatchCaptor.getValue();
        assertEquals(1L, dispatch.getOrderId());
        assertEquals("NO001", dispatch.getOrderNo());
        assertEquals(10, dispatch.getMerchantId());
        assertEquals("NaNa", dispatch.getMerchantNickName());
        assertEquals(1, dispatch.getDispatchStatus());
        assertEquals(1, dispatch.getDispatchSource());
        assertEquals(1, dispatch.getCurrentFlag());
        assertEquals(1, dispatch.getOrderStatusBefore());
        assertEquals(2, dispatch.getOrderStatusAfter());
        assertNotNull(dispatch.getDispatchTime());
        verify(mapper).updateOrderDispatch(1L, merchant, 1, 2);
        verify(mapper).insertOrderStatusFlow(
                org.mockito.ArgumentMatchers.eq(1L),
                org.mockito.ArgumentMatchers.eq(2),
                org.mockito.ArgumentMatchers.anyString());
    }

    private CancelOrderApplication buildRefundApplicationOrder() {
        CancelOrderApplication application = new CancelOrderApplication();
        application.setOrderNo("NO001");
        application.setOrderAmount(new BigDecimal("100.00"));
        application.setRefundAmount(new BigDecimal("100.00"));
        application.setOrderStatus(5);
        return application;
    }

    private AdminOrderDispatchOrderVO buildDispatchOrder() {
        AdminOrderDispatchOrderVO order = new AdminOrderDispatchOrderVO();
        order.setOrderId(1L);
        order.setOrderNo("NO001");
        order.setStatus(1);
        order.setProjectId(100L);
        order.setProjectName("按摩");
        order.setCategoryId(2);
        order.setUserLatitude(new BigDecimal("30.0000000"));
        order.setUserLongitude(new BigDecimal("120.0000000"));
        return order;
    }

    private AdminOrderDispatchMerchantVO buildDispatchMerchant() {
        AdminOrderDispatchMerchantVO merchant = new AdminOrderDispatchMerchantVO();
        merchant.setMerchantId(10);
        merchant.setMerchantName("NaNa");
        merchant.setMerchantNickName("NaNa");
        merchant.setMerchantPhone("13812345678");
        merchant.setMerchantAvatar("avatar.jpg");
        merchant.setMerchantType(0);
        merchant.setMerchantLatitude(new BigDecimal("30.0100000"));
        merchant.setMerchantLongitude(new BigDecimal("120.0100000"));
        merchant.setDistanceMeters(new BigDecimal("1000"));
        return merchant;
    }
}
