package com.storycove.repository;

import com.storycove.entity.Author;
import com.storycove.entity.Story;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

import java.util.List;
import java.util.Optional;

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

@DisplayName("Author Repository Integration Tests")
class AuthorRepositoryTest extends BaseRepositoryTest {

    @Autowired
    private AuthorRepository authorRepository;

    @Autowired
    private StoryRepository storyRepository;

    private Author author1;
    private Author author2;
    private Author author3;

    @BeforeEach
    void setUp() {
        authorRepository.deleteAll();
        storyRepository.deleteAll();

        author1 = new Author("J.R.R. Tolkien");
        author1.setNotes("Author of The Lord of the Rings");
        author1.addUrl("https://en.wikipedia.org/wiki/J._R._R._Tolkien");

        author2 = new Author("George Orwell");
        author2.setNotes("Author of 1984 and Animal Farm");

        author3 = new Author("Jane Austen");
        author3.setNotes("Author of Pride and Prejudice");

        authorRepository.saveAll(List.of(author1, author2, author3));
    }

    @Test
    @DisplayName("Should find author by name")
    void shouldFindAuthorByName() {
        Optional<Author> found = authorRepository.findByName("J.R.R. Tolkien");
        assertTrue(found.isPresent());
        assertEquals("J.R.R. Tolkien", found.get().getName());
    }

    @Test
    @DisplayName("Should check if author exists by name")
    void shouldCheckIfAuthorExistsByName() {
        assertTrue(authorRepository.existsByName("George Orwell"));
        assertFalse(authorRepository.existsByName("Unknown Author"));
    }

    @Test
    @DisplayName("Should find authors by name containing ignoring case")
    void shouldFindAuthorsByNameContainingIgnoreCase() {
        List<Author> authors = authorRepository.findByNameContainingIgnoreCase("george");
        assertEquals(1, authors.size());
        assertEquals("George Orwell", authors.get(0).getName());

        authors = authorRepository.findByNameContainingIgnoreCase("j");
        assertEquals(2, authors.size()); // J.R.R. Tolkien and Jane Austen
    }

    @Test
    @DisplayName("Should find authors by name containing with pagination")
    void shouldFindAuthorsByNameContainingWithPagination() {
        Page<Author> page = authorRepository.findByNameContainingIgnoreCase("", PageRequest.of(0, 2));
        assertEquals(2, page.getContent().size());
        assertEquals(3, page.getTotalElements());
        assertTrue(page.hasNext());
    }

    @Test
    @DisplayName("Should find authors with stories")
    void shouldFindAuthorsWithStories() {
        // Initially no authors have stories
        List<Author> authorsWithStories = authorRepository.findAuthorsWithStories();
        assertTrue(authorsWithStories.isEmpty());

        // Add a story to author1
        Story story = new Story("The Hobbit");
        author1.addStory(story);
        storyRepository.save(story);
        authorRepository.save(author1);

        authorsWithStories = authorRepository.findAuthorsWithStories();
        assertEquals(1, authorsWithStories.size());
        assertEquals("J.R.R. Tolkien", authorsWithStories.get(0).getName());
    }

    @Test
    @DisplayName("Should find authors with stories with pagination")
    void shouldFindAuthorsWithStoriesWithPagination() {
        // Add stories to authors
        Story story1 = new Story("The Hobbit");
        Story story2 = new Story("1984");
        author1.addStory(story1);
        author2.addStory(story2);
        storyRepository.saveAll(List.of(story1, story2));
        authorRepository.saveAll(List.of(author1, author2));

        Page<Author> page = authorRepository.findAuthorsWithStories(PageRequest.of(0, 1));
        assertEquals(1, page.getContent().size());
        assertEquals(2, page.getTotalElements());
    }

    @Test
    @DisplayName("Should find top rated authors")
    void shouldFindTopRatedAuthors() {
        author1.setAuthorRating(5);
        author2.setAuthorRating(5);
        author3.setAuthorRating(4);

        authorRepository.saveAll(List.of(author1, author2, author3));

        List<Author> topRated = authorRepository.findTopRatedAuthors();
        assertEquals(3, topRated.size());
        assertEquals("George Orwell", topRated.get(0).getName()); // Highest rating first
        assertEquals("J.R.R. Tolkien", topRated.get(1).getName());
        assertEquals("Jane Austen", topRated.get(2).getName());
    }

    @Test
    @DisplayName("Should find authors by minimum rating")
    void shouldFindAuthorsByMinimumRating() {
        author1.setAuthorRating(5);
        author2.setAuthorRating(5);
        author3.setAuthorRating(4);
        authorRepository.saveAll(List.of(author1, author2, author3));

        List<Author> authors = authorRepository.findAuthorsByMinimumRating(Integer.valueOf(5));
        assertEquals(2, authors.size());
    }

    @Test
    @DisplayName("Should find most prolific authors")
    void shouldFindMostProlificAuthors() {
        // Add different numbers of stories to authors
        Story story1 = new Story("The Hobbit");
        Story story2 = new Story("LOTR 1");
        Story story3 = new Story("LOTR 2");
        Story story4 = new Story("1984");

        author1.addStory(story1);
        author1.addStory(story2);
        author1.addStory(story3);
        author2.addStory(story4);

        storyRepository.saveAll(List.of(story1, story2, story3, story4));
        authorRepository.saveAll(List.of(author1, author2));

        List<Author> prolific = authorRepository.findMostProlificAuthors();
        assertEquals(2, prolific.size());
        assertEquals("J.R.R. Tolkien", prolific.get(0).getName()); // Has 3 stories
        assertEquals("George Orwell", prolific.get(1).getName()); // Has 1 story
    }

    @Test
    @DisplayName("Should find authors by URL domain")
    void shouldFindAuthorsByUrlDomain() {
        author1.addUrl("https://tolkiengateway.net/wiki/J.R.R._Tolkien");
        author2.addUrl("https://www.orwellfoundation.com/");
        authorRepository.saveAll(List.of(author1, author2));

        List<Author> authors = authorRepository.findByUrlDomain("wikipedia");
        assertEquals(1, authors.size());
        assertEquals("J.R.R. Tolkien", authors.get(0).getName());

        authors = authorRepository.findByUrlDomain("orwell");
        assertEquals(1, authors.size());
        assertEquals("George Orwell", authors.get(0).getName());
    }

    @Test
    @DisplayName("Should count recent authors")
    void shouldCountRecentAuthors() {
        java.time.LocalDateTime oneDayAgo = java.time.LocalDateTime.now().minusDays(1);
        long count = authorRepository.countRecentAuthors(oneDayAgo);
        assertEquals(3, count); // All authors are recent (created today)

        java.time.LocalDateTime now = java.time.LocalDateTime.now();
        count = authorRepository.countRecentAuthors(now);
        assertEquals(0, count); // No authors created in the future
    }

    @Test
    @DisplayName("Should save and retrieve author with all properties")
    void shouldSaveAndRetrieveAuthorWithAllProperties() {
        Author author = new Author("Test Author");
        author.setNotes("Test notes");
        author.setAvatarImagePath("/images/test-avatar.jpg");
        author.setAuthorRating(5);
        author.addUrl("https://example.com");

        Author saved = authorRepository.save(author);
        assertNotNull(saved.getId());
        
        // Force flush to ensure entity is persisted and timestamps are set
        authorRepository.flush();

        Optional<Author> retrieved = authorRepository.findById(saved.getId());
        assertTrue(retrieved.isPresent());
        Author found = retrieved.get();
        
        // Check timestamps on the retrieved entity (they should be populated after database persistence)
        assertNotNull(found.getCreatedAt());
        assertNotNull(found.getUpdatedAt());

        assertEquals("Test Author", found.getName());
        assertEquals("Test notes", found.getNotes());
        assertEquals("/images/test-avatar.jpg", found.getAvatarImagePath());
        assertEquals(5, found.getAuthorRating());
        assertEquals(1, found.getUrls().size());
        assertTrue(found.getUrls().contains("https://example.com"));
    }
}