package com.storycove.entity;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Set;

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

@DisplayName("Series Entity Tests")
class SeriesTest {

    private Validator validator;
    private Series series;

    @BeforeEach
    void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        series = new Series("The Chronicles of Narnia");
    }

    @Test
    @DisplayName("Should create series with valid name")
    void shouldCreateSeriesWithValidName() {
        assertEquals("The Chronicles of Narnia", series.getName());
        assertNotNull(series.getStories());
        assertTrue(series.getStories().isEmpty());
    }

    @Test
    @DisplayName("Should create series with name and description")
    void shouldCreateSeriesWithNameAndDescription() {
        Series seriesWithDesc = new Series("Harry Potter", "A series about a young wizard");
        assertEquals("Harry Potter", seriesWithDesc.getName());
        assertEquals("A series about a young wizard", seriesWithDesc.getDescription());
    }

    @Test
    @DisplayName("Should fail validation when name is blank")
    void shouldFailValidationWhenNameIsBlank() {
        series.setName("");
        Set<ConstraintViolation<Series>> violations = validator.validate(series);
        assertEquals(1, violations.size());
        assertEquals("Series name is required", violations.iterator().next().getMessage());
    }

    @Test
    @DisplayName("Should fail validation when name is null")
    void shouldFailValidationWhenNameIsNull() {
        series.setName(null);
        Set<ConstraintViolation<Series>> violations = validator.validate(series);
        assertEquals(1, violations.size());
        assertEquals("Series name is required", violations.iterator().next().getMessage());
    }

    @Test
    @DisplayName("Should fail validation when name exceeds 255 characters")
    void shouldFailValidationWhenNameTooLong() {
        String longName = "a".repeat(256);
        series.setName(longName);
        Set<ConstraintViolation<Series>> violations = validator.validate(series);
        assertEquals(1, violations.size());
        assertEquals("Series name must not exceed 255 characters", violations.iterator().next().getMessage());
    }

    @Test
    @DisplayName("Should fail validation when description exceeds 1000 characters")
    void shouldFailValidationWhenDescriptionTooLong() {
        String longDescription = "a".repeat(1001);
        series.setDescription(longDescription);
        Set<ConstraintViolation<Series>> violations = validator.validate(series);
        assertEquals(1, violations.size());
        assertEquals("Series description must not exceed 1000 characters", violations.iterator().next().getMessage());
    }

    @Test
    @DisplayName("Should add and remove stories correctly")
    void shouldAddAndRemoveStoriesCorrectly() {
        Story story1 = new Story("Part 1");
        Story story2 = new Story("Part 2");

        series.addStory(story1);
        series.addStory(story2);

        assertEquals(2, series.getStories().size());
        assertTrue(series.getStories().contains(story1));
        assertTrue(series.getStories().contains(story2));
        assertEquals(series, story1.getSeries());
        assertEquals(series, story2.getSeries());

        series.removeStory(story1);
        assertEquals(1, series.getStories().size());
        assertFalse(series.getStories().contains(story1));
        assertNull(story1.getSeries());
    }

    @Test
    @DisplayName("Should get next story correctly")
    void shouldGetNextStoryCorrectly() {
        Story story1 = new Story("Part 1");
        story1.setVolume(1.0);
        Story story2 = new Story("Part 2");
        story2.setVolume(2.0);
        Story story3 = new Story("Part 3");
        story3.setVolume(3.0);

        series.addStory(story1);
        series.addStory(story2);
        series.addStory(story3);

        assertEquals(story2, series.getNextStory(story1));
        assertEquals(story3, series.getNextStory(story2));
        assertNull(series.getNextStory(story3));
    }

    @Test
    @DisplayName("Should get previous story correctly")
    void shouldGetPreviousStoryCorrectly() {
        Story story1 = new Story("Part 1");
        story1.setVolume(1.0);
        Story story2 = new Story("Part 2");
        story2.setVolume(2.0);
        Story story3 = new Story("Part 3");
        story3.setVolume(3.0);

        series.addStory(story1);
        series.addStory(story2);
        series.addStory(story3);

        assertNull(series.getPreviousStory(story1));
        assertEquals(story1, series.getPreviousStory(story2));
        assertEquals(story2, series.getPreviousStory(story3));
    }

    @Test
    @DisplayName("Should return null for next/previous when volume is null")
    void shouldReturnNullForNextPreviousWhenVolumeIsNull() {
        Story storyWithoutVolume = new Story("Story without volume");
        series.addStory(storyWithoutVolume);

        assertNull(series.getNextStory(storyWithoutVolume));
        assertNull(series.getPreviousStory(storyWithoutVolume));
    }

    @Test
    @DisplayName("Should handle equals and hashCode correctly")
    void shouldHandleEqualsAndHashCodeCorrectly() {
        Series series1 = new Series("Series 1");
        Series series2 = new Series("Series 2");
        Series series3 = new Series("Series 1");

        assertNotEquals(series1, series2);
        assertNotEquals(series1, series3); // Different because IDs are null
        assertEquals(series1, series1);
        assertNotEquals(series1, null);
        assertNotEquals(series1, "not a series");

        assertEquals(series1.hashCode(), series1.hashCode());
    }

    @Test
    @DisplayName("Should have proper toString representation")
    void shouldHaveProperToStringRepresentation() {
        String toString = series.toString();
        assertTrue(toString.contains("The Chronicles of Narnia"));
        assertTrue(toString.contains("Series{"));
    }

    @Test
    @DisplayName("Should pass validation with maximum allowed lengths")
    void shouldPassValidationWithMaxAllowedLengths() {
        String maxName = "a".repeat(255);
        String maxDescription = "a".repeat(1000);
        
        series.setName(maxName);
        series.setDescription(maxDescription);
        
        Set<ConstraintViolation<Series>> violations = validator.validate(series);
        assertTrue(violations.isEmpty());
    }

}