package com.mosquito.project.persistence.entity;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.time.LocalDate;

import static org.assertj.core.api.Assertions.assertThat;

class DailyActivityStatsEntityTest {

    private DailyActivityStatsEntity entity;

    @BeforeEach
    void setUp() {
        entity = new DailyActivityStatsEntity();
    }

    @Test
    void shouldReturnNullId_whenNotSet() {
        assertThat(entity.getId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "100, 100",
        "999999, 999999",
        "0, 0"
    })
    void shouldReturnSetId_whenSetWithValue(Long id, Long expected) {
        entity.setId(id);
        assertThat(entity.getId()).isEqualTo(expected);
    }

    @Test
    void shouldReturnSetId_whenSetWithMaxValue() {
        entity.setId(Long.MAX_VALUE);
        assertThat(entity.getId()).isEqualTo(Long.MAX_VALUE);
    }

    @Test
    void shouldReturnNullActivityId_whenNotSet() {
        assertThat(entity.getActivityId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "100, 100",
        "0, 0",
        "-1, -1"
    })
    void shouldReturnSetActivityId_whenSetWithValue(Long activityId, Long expected) {
        entity.setActivityId(activityId);
        assertThat(entity.getActivityId()).isEqualTo(expected);
    }

    @Test
    void shouldReturnNullStatDate_whenNotSet() {
        assertThat(entity.getStatDate()).isNull();
    }

    @Test
    void shouldReturnSetStatDate_whenSet() {
        LocalDate date = LocalDate.of(2024, 6, 15);
        entity.setStatDate(date);
        assertThat(entity.getStatDate()).isEqualTo(date);
    }

    @Test
    void shouldHandleDifferentDates_whenSet() {
        LocalDate startOfYear = LocalDate.of(2024, 1, 1);
        LocalDate endOfYear = LocalDate.of(2024, 12, 31);
        LocalDate leapDay = LocalDate.of(2024, 2, 29);

        entity.setStatDate(startOfYear);
        assertThat(entity.getStatDate()).isEqualTo(startOfYear);

        entity.setStatDate(endOfYear);
        assertThat(entity.getStatDate()).isEqualTo(endOfYear);

        entity.setStatDate(leapDay);
        assertThat(entity.getStatDate()).isEqualTo(leapDay);
    }

    @Test
    void shouldReturnNullViews_whenNotSet() {
        assertThat(entity.getViews()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "0, 0",
        "1, 1",
        "1000, 1000",
        "999999, 999999"
    })
    void shouldReturnSetViews_whenSetWithValue(Integer views, Integer expected) {
        entity.setViews(views);
        assertThat(entity.getViews()).isEqualTo(expected);
    }

    @Test
    void shouldReturnSetViews_whenSetWithMaxValue() {
        entity.setViews(Integer.MAX_VALUE);
        assertThat(entity.getViews()).isEqualTo(Integer.MAX_VALUE);
    }

    @Test
    void shouldHandleNegativeViews_whenSet() {
        entity.setViews(-1);
        assertThat(entity.getViews()).isEqualTo(-1);
    }

    @Test
    void shouldReturnNullShares_whenNotSet() {
        assertThat(entity.getShares()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "0, 0",
        "1, 1",
        "200, 200",
        "500, 500"
    })
    void shouldReturnSetShares_whenSetWithValue(Integer shares, Integer expected) {
        entity.setShares(shares);
        assertThat(entity.getShares()).isEqualTo(expected);
    }

    @Test
    void shouldHandleLargeSharesValue_whenSet() {
        entity.setShares(1000000);
        assertThat(entity.getShares()).isEqualTo(1000000);
    }

    @Test
    void shouldReturnNullNewRegistrations_whenNotSet() {
        assertThat(entity.getNewRegistrations()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "0, 0",
        "50, 50",
        "100, 100"
    })
    void shouldReturnSetNewRegistrations_whenSetWithValue(Integer registrations, Integer expected) {
        entity.setNewRegistrations(registrations);
        assertThat(entity.getNewRegistrations()).isEqualTo(expected);
    }

    @Test
    void shouldReturnNullConversions_whenNotSet() {
        assertThat(entity.getConversions()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "0, 0",
        "10, 10",
        "25, 25"
    })
    void shouldReturnSetConversions_whenSetWithValue(Integer conversions, Integer expected) {
        entity.setConversions(conversions);
        assertThat(entity.getConversions()).isEqualTo(expected);
    }

    @Test
    void shouldAllowFieldReassignment_whenMultipleSets() {
        entity.setId(1L);
        entity.setId(2L);
        assertThat(entity.getId()).isEqualTo(2L);

        entity.setActivityId(100L);
        entity.setActivityId(200L);
        assertThat(entity.getActivityId()).isEqualTo(200L);

        entity.setViews(100);
        entity.setViews(200);
        assertThat(entity.getViews()).isEqualTo(200);
    }

    @Test
    void shouldAcceptNullValues_whenExplicitlySetToNull() {
        entity.setId(1L);
        entity.setId(null);
        assertThat(entity.getId()).isNull();

        entity.setActivityId(1L);
        entity.setActivityId(null);
        assertThat(entity.getActivityId()).isNull();

        entity.setViews(100);
        entity.setViews(null);
        assertThat(entity.getViews()).isNull();
    }

    @Test
    void shouldCreateCompleteEntity_whenAllFieldsSet() {
        entity.setId(1L);
        entity.setActivityId(100L);
        entity.setStatDate(LocalDate.of(2024, 6, 15));
        entity.setViews(1000);
        entity.setShares(200);
        entity.setNewRegistrations(50);
        entity.setConversions(10);

        assertThat(entity.getId()).isEqualTo(1L);
        assertThat(entity.getActivityId()).isEqualTo(100L);
        assertThat(entity.getStatDate()).isEqualTo(LocalDate.of(2024, 6, 15));
        assertThat(entity.getViews()).isEqualTo(1000);
        assertThat(entity.getShares()).isEqualTo(200);
        assertThat(entity.getNewRegistrations()).isEqualTo(50);
        assertThat(entity.getConversions()).isEqualTo(10);
    }

    @Test
    void shouldHandleBoundaryValues_whenSetToZero() {
        entity.setViews(0);
        entity.setShares(0);
        entity.setNewRegistrations(0);
        entity.setConversions(0);

        assertThat(entity.getViews()).isZero();
        assertThat(entity.getShares()).isZero();
        assertThat(entity.getNewRegistrations()).isZero();
        assertThat(entity.getConversions()).isZero();
    }

    @Test
    void shouldHandleLargeValues_whenSetToMax() {
        entity.setViews(Integer.MAX_VALUE);
        entity.setShares(Integer.MAX_VALUE);
        entity.setNewRegistrations(Integer.MAX_VALUE);
        entity.setConversions(Integer.MAX_VALUE);

        assertThat(entity.getViews()).isEqualTo(Integer.MAX_VALUE);
        assertThat(entity.getShares()).isEqualTo(Integer.MAX_VALUE);
        assertThat(entity.getNewRegistrations()).isEqualTo(Integer.MAX_VALUE);
        assertThat(entity.getConversions()).isEqualTo(Integer.MAX_VALUE);
    }

    @Test
    void shouldHandleNegativeValues_whenSet() {
        entity.setViews(-100);
        entity.setShares(-50);
        entity.setNewRegistrations(-25);
        entity.setConversions(-10);

        assertThat(entity.getViews()).isEqualTo(-100);
        assertThat(entity.getShares()).isEqualTo(-50);
        assertThat(entity.getNewRegistrations()).isEqualTo(-25);
        assertThat(entity.getConversions()).isEqualTo(-10);
    }

    @Test
    void shouldHandleEpochDate_whenSet() {
        LocalDate epoch = LocalDate.of(1970, 1, 1);
        entity.setStatDate(epoch);
        assertThat(entity.getStatDate()).isEqualTo(epoch);
    }

    @Test
    void shouldHandleFutureDate_whenSet() {
        LocalDate future = LocalDate.of(2099, 12, 31);
        entity.setStatDate(future);
        assertThat(entity.getStatDate()).isEqualTo(future);
    }

    @ParameterizedTest
    @ValueSource(ints = {1, 10, 100, 1000, 10000, 100000})
    void shouldAcceptVariousActivityIds_whenSet(int activityId) {
        entity.setActivityId((long) activityId);
        assertThat(entity.getActivityId()).isEqualTo(activityId);
    }

    @Test
    void shouldMaintainConsistency_whenUsedWithActivityEntity() {
        ActivityEntity activity = new ActivityEntity();
        activity.setId(100L);

        entity.setActivityId(activity.getId());
        entity.setStatDate(LocalDate.now());
        entity.setViews(1000);

        assertThat(entity.getActivityId()).isEqualTo(activity.getId());
    }

    @Test
    void shouldSupportFluentSetterChaining_whenMultipleSets() {
        entity.setId(1L);
        entity.setActivityId(100L);
        entity.setStatDate(LocalDate.of(2024, 1, 1));
        entity.setViews(1000);
        entity.setShares(200);
        entity.setNewRegistrations(50);
        entity.setConversions(10);

        assertThat(entity.getId()).isEqualTo(1L);
        assertThat(entity.getActivityId()).isEqualTo(100L);
    }

    @Test
    void shouldHandleDateRangeAcrossMonths_whenSet() {
        LocalDate endOfMonth = LocalDate.of(2024, 1, 31);
        LocalDate startOfNextMonth = LocalDate.of(2024, 2, 1);

        entity.setStatDate(endOfMonth);
        assertThat(entity.getStatDate().getDayOfMonth()).isEqualTo(31);

        entity.setStatDate(startOfNextMonth);
        assertThat(entity.getStatDate().getDayOfMonth()).isEqualTo(1);
    }

    @Test
    void shouldCalculateCorrectMetricsRatio_whenViewsAndConversionsSet() {
        entity.setViews(1000);
        entity.setConversions(100);

        double conversionRate = (double) entity.getConversions() / entity.getViews();
        assertThat(conversionRate).isEqualTo(0.1);
    }

    @Test
    void shouldHandleEmptyEntityState_whenNoFieldsSet() {
        assertThat(entity.getId()).isNull();
        assertThat(entity.getActivityId()).isNull();
        assertThat(entity.getStatDate()).isNull();
        assertThat(entity.getViews()).isNull();
        assertThat(entity.getShares()).isNull();
        assertThat(entity.getNewRegistrations()).isNull();
        assertThat(entity.getConversions()).isNull();
    }

    @Test
    void shouldAcceptPartialEntityState_whenSomeFieldsSet() {
        entity.setActivityId(1L);
        entity.setStatDate(LocalDate.now());

        assertThat(entity.getId()).isNull();
        assertThat(entity.getActivityId()).isEqualTo(1L);
        assertThat(entity.getViews()).isNull();
    }
}
