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("Author Entity Tests")
class AuthorTest {

    private Validator validator;
    private Author author;

    @BeforeEach
    void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        author = new Author("Test Author");
    }

    @Test
    @DisplayName("Should create author with valid name")
    void shouldCreateAuthorWithValidName() {
        assertEquals("Test Author", author.getName());
        assertNotNull(author.getStories());
        assertNotNull(author.getUrls());
        assertNull(author.getAuthorRating());
    }

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

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

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

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

        author.addStory(story1);
        author.addStory(story2);

        assertEquals(2, author.getStories().size());
        assertTrue(author.getStories().contains(story1));
        assertTrue(author.getStories().contains(story2));
        assertEquals(author, story1.getAuthor());
        assertEquals(author, story2.getAuthor());

        author.removeStory(story1);
        assertEquals(1, author.getStories().size());
        assertFalse(author.getStories().contains(story1));
        assertNull(story1.getAuthor());
    }

    @Test
    @DisplayName("Should add and remove URLs correctly")
    void shouldAddAndRemoveUrlsCorrectly() {
        String url1 = "https://example.com/author1";
        String url2 = "https://example.com/author2";

        author.addUrl(url1);
        author.addUrl(url2);

        assertEquals(2, author.getUrls().size());
        assertTrue(author.getUrls().contains(url1));
        assertTrue(author.getUrls().contains(url2));

        author.addUrl(url1); // Should not add duplicate
        assertEquals(2, author.getUrls().size());

        author.removeUrl(url1);
        assertEquals(1, author.getUrls().size());
        assertFalse(author.getUrls().contains(url1));
    }

    @Test
    @DisplayName("Should not add null or duplicate URLs")
    void shouldNotAddNullOrDuplicateUrls() {
        String url = "https://example.com/author";

        author.addUrl(null);
        assertEquals(0, author.getUrls().size());

        author.addUrl(url);
        author.addUrl(url);
        assertEquals(1, author.getUrls().size());
    }

    @Test
    @DisplayName("Should set author rating correctly")
    void shouldSetAuthorRatingCorrectly() {
        author.setAuthorRating(4);
        assertEquals(4, author.getAuthorRating());

        author.setAuthorRating(5);
        assertEquals(5, author.getAuthorRating());

        author.setAuthorRating(null);
        assertNull(author.getAuthorRating());
    }

    @Test
    @DisplayName("Should handle equals and hashCode correctly")
    void shouldHandleEqualsAndHashCodeCorrectly() {
        Author author1 = new Author("Author 1");
        Author author2 = new Author("Author 2");
        Author author3 = new Author("Author 1");

        assertNotEquals(author1, author2);
        assertNotEquals(author1, author3); // Different because IDs are null
        assertEquals(author1, author1);
        assertNotEquals(author1, null);
        assertNotEquals(author1, "not an author");

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

    @Test
    @DisplayName("Should have proper toString representation")
    void shouldHaveProperToStringRepresentation() {
        String toString = author.toString();
        assertTrue(toString.contains("Test Author"));
        assertTrue(toString.contains("Author{"));
    }
}