package com.ruoyi.info.collection.service;

import com.ruoyi.info.collection.domain.CcdiStaffRecruitment;
import com.ruoyi.info.collection.domain.CcdiStaffRecruitmentWork;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentWorkExcel;
import com.ruoyi.info.collection.domain.vo.RecruitmentImportFailureVO;
import com.ruoyi.info.collection.mapper.CcdiStaffRecruitmentMapper;
import com.ruoyi.info.collection.mapper.CcdiStaffRecruitmentWorkMapper;
import com.ruoyi.info.collection.service.impl.CcdiStaffRecruitmentImportServiceImpl;
import java.util.Collections;
import java.util.List;
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;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiStaffRecruitmentImportServiceImplTest {

    @InjectMocks
    private CcdiStaffRecruitmentImportServiceImpl service;

    @Mock
    private CcdiStaffRecruitmentMapper recruitmentMapper;

    @Mock
    private CcdiStaffRecruitmentWorkMapper recruitmentWorkMapper;

    @Mock
    private RedisTemplate<String, Object> redisTemplate;

    @Mock
    private ValueOperations<String, Object> valueOperations;

    @Mock
    private HashOperations<String, Object, Object> hashOperations;

    @Test
    void shouldFailWholeWorkGroupWhenExistingHistoryExists() {
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(recruitmentMapper.selectList(any())).thenReturn(List.of(buildRecruitment("RC001")));
        when(recruitmentWorkMapper.selectCount(any())).thenReturn(1L);

        CcdiStaffRecruitmentWorkExcel workRow = new CcdiStaffRecruitmentWorkExcel();
        workRow.setRecruitId("RC001");
        workRow.setCandName("张三");
        workRow.setRecruitName("社会招聘项目");
        workRow.setPosName("Java工程师");
        workRow.setSortOrder(1);
        workRow.setCompanyName("测试科技");
        workRow.setPositionName("开发工程师");
        workRow.setJobStartMonth("2022-01");

        service.importRecruitmentAsync(Collections.emptyList(), List.of(workRow), "task-1", "admin");

        verify(recruitmentWorkMapper, never()).delete(any());
        verify(recruitmentWorkMapper, never()).insert(any(com.ruoyi.info.collection.domain.CcdiStaffRecruitmentWork.class));

        ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
        verify(valueOperations).set(eq("import:recruitment:task-1:failures"), failureCaptor.capture(), anyLong(), any());
        Object rawFailures = failureCaptor.getValue();
        assertNotNull(rawFailures);
        assertInstanceOf(List.class, rawFailures);
        List<?> failures = (List<?>) rawFailures;
        assertFalse(failures.isEmpty());
        RecruitmentImportFailureVO failure = (RecruitmentImportFailureVO) failures.get(0);
        assertEquals("历史工作经历", failure.getSheetName());
        assertEquals("2", failure.getSheetRowNum());
        assertEquals("招聘记录编号[RC001]已存在历史工作经历，不允许重复导入", failure.getErrorMessage());
    }

    @Test
    void shouldAllowDuplicateRecruitIdsWhenImportingMainSheet() {
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(recruitmentMapper.insert(any(CcdiStaffRecruitment.class))).thenReturn(1);

        CcdiStaffRecruitmentExcel first = buildRecruitmentExcel("RC001", "张三");
        CcdiStaffRecruitmentExcel second = buildRecruitmentExcel("RC001", "李四");

        service.importRecruitmentAsync(List.of(first, second), Collections.emptyList(), "task-2", "admin");

        verify(recruitmentMapper, times(2)).insert(any(CcdiStaffRecruitment.class));
        verify(valueOperations, never()).set(eq("import:recruitment:task-2:failures"), any(), anyLong(), any());
    }

    @Test
    void shouldAttachWorkToMatchedRecruitmentWhenRecruitIdIsDuplicated() {
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        CcdiStaffRecruitment matched = buildRecruitment(10L, "RC001", "张三");
        CcdiStaffRecruitment other = buildRecruitment(11L, "RC001", "李四");
        when(recruitmentMapper.selectList(any())).thenReturn(List.of(matched, other));
        when(recruitmentWorkMapper.selectCount(any())).thenReturn(0L);

        CcdiStaffRecruitmentWorkExcel workRow = buildWorkExcel("RC001", "张三");

        service.importRecruitmentAsync(Collections.emptyList(), List.of(workRow), "task-3", "admin");

        ArgumentCaptor<CcdiStaffRecruitmentWork> workCaptor = ArgumentCaptor.forClass(CcdiStaffRecruitmentWork.class);
        verify(recruitmentWorkMapper).insert(workCaptor.capture());
        assertEquals(10L, workCaptor.getValue().getRecruitmentId());
        assertEquals("RC001", workCaptor.getValue().getRecruitId());
    }

    private CcdiStaffRecruitment buildRecruitment(String recruitId) {
        return buildRecruitment(1L, recruitId, "张三");
    }

    private CcdiStaffRecruitment buildRecruitment(Long id, String recruitId, String candName) {
        CcdiStaffRecruitment recruitment = new CcdiStaffRecruitment();
        recruitment.setId(id);
        recruitment.setRecruitId(recruitId);
        recruitment.setRecruitType("SOCIAL");
        recruitment.setCandName(candName);
        recruitment.setRecruitName("社会招聘项目");
        recruitment.setPosName("Java工程师");
        return recruitment;
    }

    private CcdiStaffRecruitmentExcel buildRecruitmentExcel(String recruitId, String candName) {
        CcdiStaffRecruitmentExcel excel = new CcdiStaffRecruitmentExcel();
        excel.setRecruitId(recruitId);
        excel.setRecruitName("社会招聘项目");
        excel.setPosName("Java工程师");
        excel.setPosCategory("技术类");
        excel.setPosDesc("负责系统开发");
        excel.setAdmitStatus("录用");
        excel.setCandName(candName);
        excel.setRecruitType("SOCIAL");
        excel.setCandEdu("本科");
        excel.setCandId(candName.equals("张三") ? "110105199001010010" : "110105199002020026");
        excel.setCandGrad("202110");
        excel.setCandSchool("四川大学");
        excel.setCandMajor("法学");
        return excel;
    }

    private CcdiStaffRecruitmentWorkExcel buildWorkExcel(String recruitId, String candName) {
        CcdiStaffRecruitmentWorkExcel workRow = new CcdiStaffRecruitmentWorkExcel();
        workRow.setRecruitId(recruitId);
        workRow.setCandName(candName);
        workRow.setRecruitName("社会招聘项目");
        workRow.setPosName("Java工程师");
        workRow.setSortOrder(1);
        workRow.setCompanyName("测试科技");
        workRow.setPositionName("开发工程师");
        workRow.setJobStartMonth("2022-01");
        return workRow;
    }
}
