package org.springblade.modules.martial.service.impl;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springblade.modules.martial.mapper.*;
import org.springblade.modules.martial.pojo.dto.ScheduleResultDTO;
import org.springblade.modules.martial.pojo.entity.*;
import org.springblade.modules.martial.pojo.vo.ScheduleGroupDetailVO;
import org.springblade.modules.martial.service.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("ScheduleQueryService Unit Tests")
class ScheduleQueryServiceImplTest {

    @Mock
    private MartialScheduleGroupMapper scheduleGroupMapper;
    
    @Mock
    private MartialAthleteMapper athleteMapper;
    
    @Mock
    private MartialTeamMapper teamMapper;
    
    @Mock
    private MartialTeamMemberMapper teamMemberMapper;
    
    @Mock
    private IMartialProjectService projectService;
    
    @Mock
    private IMartialAthleteService athleteService;
    
    @Mock
    private IMartialVenueService venueService;

    @InjectMocks
    private ScheduleQueryServiceImpl scheduleQueryService;

    @Test
    @DisplayName("Should return empty result when no schedule details")
    void shouldReturnEmptyResultWhenNoDetails() {
        Long competitionId = 1L;
        when(scheduleGroupMapper.selectScheduleGroupDetails(competitionId)).thenReturn(new ArrayList<>());
        when(projectService.list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class))).thenReturn(new ArrayList<>());

        ScheduleResultDTO result = scheduleQueryService.getScheduleResult(competitionId);

        assertNotNull(result);
        assertTrue(result.getIsDraft());
        assertFalse(result.getIsCompleted());
        verify(scheduleGroupMapper).selectScheduleGroupDetails(competitionId);
    }

    @Test
    @DisplayName("Should return schedule result with groups")
    void shouldReturnScheduleResultWithGroups() {
        Long competitionId = 1L;
        List<ScheduleGroupDetailVO> details = createMockScheduleDetails();
        
        when(scheduleGroupMapper.selectScheduleGroupDetails(competitionId)).thenReturn(details);

        ScheduleResultDTO result = scheduleQueryService.getScheduleResult(competitionId);

        assertNotNull(result);
        assertNotNull(result.getCompetitionGroups());
        verify(scheduleGroupMapper).selectScheduleGroupDetails(competitionId);
    }

    @Test
    @DisplayName("Should mark result as completed when status is completed")
    void shouldMarkResultAsCompletedWhenStatusIsCompleted() {
        Long competitionId = 1L;
        List<ScheduleGroupDetailVO> details = createMockScheduleDetails();
        details.get(0).setScheduleStatus("completed");
        
        when(scheduleGroupMapper.selectScheduleGroupDetails(competitionId)).thenReturn(details);

        ScheduleResultDTO result = scheduleQueryService.getScheduleResult(competitionId);

        assertNotNull(result);
        assertFalse(result.getIsDraft());
        assertTrue(result.getIsCompleted());
    }

    @Test
    @DisplayName("Should mark result as draft when status is not completed")
    void shouldMarkResultAsDraftWhenStatusIsNotCompleted() {
        Long competitionId = 1L;
        List<ScheduleGroupDetailVO> details = createMockScheduleDetails();
        details.get(0).setScheduleStatus("draft");
        
        when(scheduleGroupMapper.selectScheduleGroupDetails(competitionId)).thenReturn(details);

        ScheduleResultDTO result = scheduleQueryService.getScheduleResult(competitionId);

        assertNotNull(result);
        assertTrue(result.getIsDraft());
        assertFalse(result.getIsCompleted());
    }

    @Test
    @DisplayName("Should handle null competition ID")
    void shouldHandleNullCompetitionId() {
        when(scheduleGroupMapper.selectScheduleGroupDetails(null)).thenReturn(new ArrayList<>());
        when(projectService.list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class))).thenReturn(new ArrayList<>());

        ScheduleResultDTO result = scheduleQueryService.getScheduleResult(null);

        assertNotNull(result);
    }

    @Test
    @DisplayName("Should generate initial schedule when no details exist")
    void shouldGenerateInitialScheduleWhenNoDetailsExist() {
        Long competitionId = 1L;
        when(scheduleGroupMapper.selectScheduleGroupDetails(competitionId)).thenReturn(new ArrayList<>());
        when(projectService.list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class))).thenReturn(createMockProjects());
        when(athleteService.list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class))).thenReturn(createMockAthletes());
        when(venueService.list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class))).thenReturn(createMockVenues());

        ScheduleResultDTO result = scheduleQueryService.getScheduleResult(competitionId);

        assertNotNull(result);
        assertTrue(result.getIsDraft());
        verify(projectService).list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class));
        verify(athleteService).list(any(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class));
    }

    private List<ScheduleGroupDetailVO> createMockScheduleDetails() {
        List<ScheduleGroupDetailVO> details = new ArrayList<>();
        ScheduleGroupDetailVO detail = new ScheduleGroupDetailVO();
        detail.setGroupId(1L);
        detail.setGroupName("Test Group");
        detail.setVenueId(10L);
        detail.setVenueName("Test Venue");
        detail.setParticipantId(100L);
        detail.setPlayerName("Test Player");
        detail.setOrganization("Test Org");
        detail.setDisplayOrder(1);
        detail.setProjectType(1);
        detail.setScheduleStatus("draft");
        details.add(detail);
        return details;
    }

    private List<MartialProject> createMockProjects() {
        List<MartialProject> projects = new ArrayList<>();
        MartialProject project = new MartialProject();
        project.setId(1L);
        project.setProjectName("Test Project");
        project.setType(1);
        projects.add(project);
        return projects;
    }

    private List<MartialAthlete> createMockAthletes() {
        List<MartialAthlete> athletes = new ArrayList<>();
        MartialAthlete athlete = new MartialAthlete();
        athlete.setId(1L);
        athlete.setPlayerName("Test Athlete");
        athlete.setProjectId(1L);
        athletes.add(athlete);
        return athletes;
    }

    private List<MartialVenue> createMockVenues() {
        List<MartialVenue> venues = new ArrayList<>();
        MartialVenue venue = new MartialVenue();
        venue.setId(1L);
        venue.setVenueName("Test Venue");
        venues.add(venue);
        return venues;
    }
}
