package com.starry.admin.modules.order.service.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IOrderLifecycleService;
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class PlayOrderInfoServiceImplTest {

    @InjectMocks
    private PlayOrderInfoServiceImpl service;

    @Mock
    private PlayOrderInfoMapper baseMapper;

    @Mock
    private IPlayClerkUserInfoService playClerkUserInfoService;

    @Mock
    private IPlayPersonnelGroupInfoService playClerkGroupInfoService;

    @Mock
    private IOrderLifecycleService orderLifecycleService;

    @Test
    void completeOrderByManagement_allowsAdmin() {
        PlayOrderInfoEntity order = new PlayOrderInfoEntity();
        order.setId("order-1");
        order.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
        when(baseMapper.selectById("order-1")).thenReturn(order);

        String remark = "人工完成";
        service.completeOrderByManagement(OperatorType.ADMIN.getCode(), "sys-admin", order.getId(), remark);

        ArgumentCaptor<OrderCompletionContext> captor = ArgumentCaptor.forClass(OrderCompletionContext.class);
        verify(orderLifecycleService).completeOrder(eq(order.getId()), captor.capture());
        OrderCompletionContext context = captor.getValue();
        assertEquals(OrderActor.ADMIN, context.getOperatorActor());
        assertEquals("sys-admin", context.getOperatorId());
        assertEquals(OrderTriggerSource.WX_CLERK_MGMT, context.getTriggerSource());
        assertEquals(remark, context.getComment());
    }

    @Test
    void completeOrderByManagement_deniesLeaderOutsideGroup() {
        PlayOrderInfoEntity order = new PlayOrderInfoEntity();
        order.setId("order-2");
        order.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
        order.setGroupId("group-A");
        when(baseMapper.selectById("order-2")).thenReturn(order);

        PlayClerkUserInfoEntity leader = new PlayClerkUserInfoEntity();
        leader.setId("leader-clerk");
        leader.setSysUserId("leader-sys");
        when(playClerkUserInfoService.getById("leader-clerk")).thenReturn(leader);

        PlayPersonnelGroupInfoEntity leaderGroup = new PlayPersonnelGroupInfoEntity();
        leaderGroup.setId("group-B");
        when(playClerkGroupInfoService.selectByUserId("leader-sys")).thenReturn(leaderGroup);

        assertThrows(
                CustomException.class,
                () -> service.completeOrderByManagement(
                        OperatorType.GROUP_LEADER.getCode(), "leader-clerk", order.getId(), null));
        verify(orderLifecycleService, never()).completeOrder(eq(order.getId()), any());
    }

    @Test
    void completeOrderByManagement_allowsLeaderInGroup() {
        PlayOrderInfoEntity order = new PlayOrderInfoEntity();
        order.setId("order-3");
        order.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
        order.setGroupId("group-C");
        order.setAcceptBy("clerk-accepted");
        when(baseMapper.selectById("order-3")).thenReturn(order);

        PlayClerkUserInfoEntity leader = new PlayClerkUserInfoEntity();
        leader.setId("leader-clerk");
        leader.setSysUserId("leader-sys");
        when(playClerkUserInfoService.getById("leader-clerk")).thenReturn(leader);

        PlayPersonnelGroupInfoEntity leaderGroup = new PlayPersonnelGroupInfoEntity();
        leaderGroup.setId("group-C");
        when(playClerkGroupInfoService.selectByUserId("leader-sys")).thenReturn(leaderGroup);

        service.completeOrderByManagement(
                OperatorType.GROUP_LEADER.getCode(), "leader-clerk", order.getId(), null);

        ArgumentCaptor<OrderCompletionContext> captor = ArgumentCaptor.forClass(OrderCompletionContext.class);
        verify(orderLifecycleService).completeOrder(eq(order.getId()), captor.capture());
        OrderCompletionContext context = captor.getValue();
        assertEquals(OrderActor.GROUP_LEADER, context.getOperatorActor());
        assertEquals("leader-clerk", context.getOperatorId());
        assertEquals(OrderTriggerSource.WX_CLERK_MGMT, context.getTriggerSource());
        assertNull(context.getComment());
    }

    @Test
    void completeOrderByManagement_promotesAcceptedToInProgress() {
        PlayOrderInfoEntity accepted = new PlayOrderInfoEntity();
        accepted.setId("order-4");
        accepted.setOrderStatus(OrderStatus.ACCEPTED.getCode());
        PlayOrderInfoEntity inProgress = new PlayOrderInfoEntity();
        inProgress.setId("order-4");
        inProgress.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
        when(baseMapper.selectById("order-4")).thenReturn(accepted, inProgress);

        service.completeOrderByManagement(OperatorType.ADMIN.getCode(), "sys-admin", accepted.getId(), null);

        ArgumentCaptor<PlayOrderInfoEntity> updateCaptor = ArgumentCaptor.forClass(PlayOrderInfoEntity.class);
        verify(baseMapper).updateById(updateCaptor.capture());
        PlayOrderInfoEntity updateEntity = updateCaptor.getValue();
        assertEquals(OrderStatus.IN_PROGRESS.getCode(), updateEntity.getOrderStatus());

        ArgumentCaptor<OrderCompletionContext> completionCaptor = ArgumentCaptor.forClass(OrderCompletionContext.class);
        verify(orderLifecycleService).completeOrder(eq(accepted.getId()), completionCaptor.capture());
        assertEquals(OrderTriggerSource.WX_CLERK_MGMT, completionCaptor.getValue().getTriggerSource());
    }
}
