package org.springblade.modules.martial;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springblade.modules.martial.pojo.entity.MartialScheduleAthlete;

import static org.junit.jupiter.api.Assertions.*;

/**
 * MartialScheduleAthleteService Unit Tests
 * @author QA Team
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("Schedule Athlete Service Tests")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class MartialScheduleAthleteServiceTest {

    private MartialScheduleAthlete validScheduleAthlete;

    @BeforeEach
    void setUp() {
        validScheduleAthlete = createValidScheduleAthlete();
    }

    private MartialScheduleAthlete createValidScheduleAthlete() {
        MartialScheduleAthlete sa = new MartialScheduleAthlete();
        sa.setId(1L);
        sa.setScheduleId(100L);
        sa.setAthleteId(10L);
        sa.setCompetitionId(1L);
        sa.setOrderNum(1);
        sa.setIsCompleted(0);
        sa.setIsRefereed(0);
        sa.setStatus(0);
        return sa;
    }

    @Nested
    @DisplayName("Entity Validation Tests")
    class EntityValidationTests {
        @Test
        @DisplayName("Valid schedule athlete has required fields")
        void test_validEntity_hasRequiredFields() {
            assertAll("Required fields",
                () -> assertNotNull(validScheduleAthlete.getScheduleId()),
                () -> assertNotNull(validScheduleAthlete.getAthleteId()),
                () -> assertNotNull(validScheduleAthlete.getCompetitionId()),
                () -> assertNotNull(validScheduleAthlete.getOrderNum())
            );
        }

        @ParameterizedTest
        @ValueSource(ints = {0, 1})
        @DisplayName("IsCompleted should be 0 or 1")
        void test_isCompleted_validValues(int value) {
            validScheduleAthlete.setIsCompleted(value);
            assertTrue(value == 0 || value == 1);
        }

        @ParameterizedTest
        @ValueSource(ints = {0, 1})
        @DisplayName("IsRefereed should be 0 or 1")
        void test_isRefereed_validValues(int value) {
            validScheduleAthlete.setIsRefereed(value);
            assertTrue(value == 0 || value == 1);
        }
    }

    @Nested
    @DisplayName("Order Number Tests")
    class OrderNumberTests {
        @Test
        @DisplayName("Order number should be positive")
        void test_orderNum_positive() {
            assertTrue(validScheduleAthlete.getOrderNum() > 0);
        }

        @ParameterizedTest
        @ValueSource(ints = {1, 2, 3, 10, 50, 100})
        @DisplayName("Valid order numbers")
        void test_orderNum_validValues(int orderNum) {
            validScheduleAthlete.setOrderNum(orderNum);
            assertTrue(orderNum > 0);
        }

        @Test
        @DisplayName("Order numbers should be unique per schedule")
        void test_orderNum_uniquePerSchedule() {
            MartialScheduleAthlete sa1 = createValidScheduleAthlete();
            sa1.setOrderNum(1);
            MartialScheduleAthlete sa2 = createValidScheduleAthlete();
            sa2.setOrderNum(2);
            assertNotEquals(sa1.getOrderNum(), sa2.getOrderNum());
        }
    }

    @Nested
    @DisplayName("Status Transition Tests")
    class StatusTransitionTests {
        @Test
        @DisplayName("Mark as completed")
        void test_markAsCompleted() {
            validScheduleAthlete.setIsCompleted(1);
            assertEquals(1, validScheduleAthlete.getIsCompleted());
        }

        @Test
        @DisplayName("Mark as refereed")
        void test_markAsRefereed() {
            validScheduleAthlete.setIsRefereed(1);
            assertEquals(1, validScheduleAthlete.getIsRefereed());
        }

        @Test
        @DisplayName("Complete flow: not started -> completed -> refereed")
        void test_completeFlow() {
            assertEquals(0, validScheduleAthlete.getIsCompleted());
            assertEquals(0, validScheduleAthlete.getIsRefereed());
            validScheduleAthlete.setIsCompleted(1);
            assertEquals(1, validScheduleAthlete.getIsCompleted());
            validScheduleAthlete.setIsRefereed(1);
            assertEquals(1, validScheduleAthlete.getIsRefereed());
        }
    }

    @Nested
    @DisplayName("Conflict Detection Tests")
    class ConflictDetectionTests {
        @Test
        @DisplayName("Same athlete in different schedules")
        void test_sameAthleteMultipleSchedules() {
            MartialScheduleAthlete sa1 = createValidScheduleAthlete();
            sa1.setScheduleId(100L);
            sa1.setAthleteId(10L);
            MartialScheduleAthlete sa2 = createValidScheduleAthlete();
            sa2.setScheduleId(200L);
            sa2.setAthleteId(10L);
            assertEquals(sa1.getAthleteId(), sa2.getAthleteId());
            assertNotEquals(sa1.getScheduleId(), sa2.getScheduleId());
        }

        @Test
        @DisplayName("Detect duplicate athlete in same schedule")
        void test_duplicateAthleteInSchedule() {
            MartialScheduleAthlete sa1 = createValidScheduleAthlete();
            MartialScheduleAthlete sa2 = createValidScheduleAthlete();
            boolean isDuplicate = sa1.getScheduleId().equals(sa2.getScheduleId()) 
                && sa1.getAthleteId().equals(sa2.getAthleteId());
            assertTrue(isDuplicate);
        }
    }
}
