import org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Assertions.fail;
import java.util.List;

/**
 * JUnit tests for the Backend class functionality
 */
public class BackendTests {

    /**
     * Test 1: Tests basic data loading and range filtering functionality
     * This test verifies that:
     * - readData() can load songs from the songs.csv file
     * - getAndSetRange() correctly filters songs by year range
     * - The backend properly handles null values for range boundaries
     */
    @Test
    public void backendTest1() {
        // Create backend with placeholder tree
        Tree_Placeholder tree = new Tree_Placeholder();
        Backend backend = new Backend(tree);
        
        try {
            // Load data from the actual songs.csv file
            backend.readData("songs.csv");
            
            // Test 1a: Get songs from year 2010 only
            List<String> songs2010 = backend.getAndSetRange(2010, 2010);
            // With the actual data, we should get songs specifically from 2010
            
            // Test 1b: Test unbounded upper range (null high parameter) - songs from 2010 onward
            List<String> songsAfter2009 = backend.getAndSetRange(2010, null);
            
            // Test 1c: Test unbounded lower range (null low parameter) - songs up to 2010
            List<String> songsBefore2011 = backend.getAndSetRange(null, 2010);
            
            // Test 1d: Test completely unbounded range - all songs
            List<String> allSongs = backend.getAndSetRange(null, null);
            
            // Basic validation - with actual data, we can check for expected results
            // All songs in the provided CSV are from 2010, so all lists should contain songs
            Assert.assertFalse("2010 songs list should not be empty", songs2010.isEmpty());
            Assert.assertFalse("Songs after 2009 should not be empty", songsAfter2009.isEmpty());
            Assert.assertFalse("Songs before 2011 should not be empty", songsBefore2011.isEmpty());
            Assert.assertFalse("All songs list should not be empty", allSongs.isEmpty());
            
        } catch (Exception e) {
            Assert.fail("Test 1 failed with exception: " + e.getMessage());
        }
    }

    /**
     * Test 2: Tests loudness filtering and combination with year range filtering
     * This test verifies that:
     * - applyAndSetFilter() correctly filters songs by loudness threshold
     * - Filters combine properly with existing year ranges
     * - Null threshold correctly clears the loudness filter
     */
    @Test
    public void backendTest2() {
        // Create backend with placeholder tree
        Tree_Placeholder tree = new Tree_Placeholder();
        Backend backend = new Backend(tree);
        
        try {
            // Load data from the actual songs.csv file
            backend.readData("songs.csv");
            
            // Set a year range first (all songs are 2010 in our data)
            backend.getAndSetRange(2010, 2010);
            
            // Test 2a: Apply loudness filter with threshold (-4 dB)
            List<String> quietSongs = backend.applyAndSetFilter(-4);
            // Should return songs with loudness < -4 dB from 2010
            
            // Test 2b: Apply different threshold (-6 dB)
            List<String> veryQuietSongs = backend.applyAndSetFilter(-6);
            // Should return songs with loudness < -6 dB from 2010
            
            // Test 2c: Clear loudness filter with null threshold
            List<String> allSongsInRange = backend.applyAndSetFilter(null);
            // Should return all songs from 2010 (no loudness filter)
            
            // Test 2d: Test filter without prior range setting
            Backend backend2 = new Backend(new Tree_Placeholder());
            backend2.readData("songs.csv");
            List<String> allQuietSongs = backend2.applyAndSetFilter(-5);
            // Should return all songs with loudness < -5 dB (no year range set)
            
            // With actual data, we can validate filter results
            // Based on the CSV data, we know there are songs with different loudness values
            Assert.assertNotNull("Quiet songs list should not be null", quietSongs);
            Assert.assertNotNull("Very quiet songs list should not be null", veryQuietSongs);
            Assert.assertFalse("All songs in range should not be empty", allSongsInRange.isEmpty());
            
        } catch (Exception e) {
            Assert.fail("Test 2 failed with exception: " + e.getMessage());
        }
    }

    /**
     * Test 3: Tests the fiveMost() method and complex filter combinations
     * This test verifies that:
     * - fiveMost() returns the most danceable songs considering current filters
     * - The method respects both year range and loudness filters
     * - Returns up to 5 songs as specified
     * - Handles cases with fewer than 5 matching songs
     */
    @Test
    public void backendTest3() {
        // Create backend with placeholder tree
        Tree_Placeholder tree = new Tree_Placeholder();
        Backend backend = new Backend(tree);
        
        try {
            // Load data from the actual songs.csv file
            backend.readData("songs.csv");
            
            // Test 3a: Get top 5 most danceable songs without filters
            List<String> topDanceable = backend.fiveMost();
            // Should return up to 5 songs, most danceable first from the 30 songs in CSV
            
            // Test 3b: Get top danceable songs with year range filter (all songs are 2010)
            backend.getAndSetRange(2010, 2010);
            List<String> topDanceableInRange = backend.fiveMost();
            // Should return most danceable songs from 2010
            
            // Test 3c: Get top danceable songs with both year range and loudness filter
            backend.applyAndSetFilter(-4); // Songs quieter than -4 dB
            List<String> topDanceableFiltered = backend.fiveMost();
            // Should return most danceable songs from 2010 with loudness < -4 dB
            
            // Test 3d: Test with very restrictive filters
            backend.getAndSetRange(2015, 2015); // No songs in this range in our data
            backend.applyAndSetFilter(-10); // Very restrictive loudness filter
            List<String> emptyResult = backend.fiveMost();
            // Should return empty list when no songs match filters
            
            // Validate specific expectations with actual data
            Assert.assertTrue("Should not return more than 5 songs", topDanceable.size() <= 5);
            Assert.assertTrue("Should not return more than 5 songs", topDanceableInRange.size() <= 5);
            Assert.assertTrue("Should not return more than 5 songs", topDanceableFiltered.size() <= 5);
            
            // Since we have 30 songs in the CSV, we should get exactly 5 for unfiltered queries
            Assert.assertEquals("Should return exactly 5 most danceable songs", 5, topDanceable.size());
            
            // The empty result test should return an empty list
            Assert.assertTrue("Should return empty list for no matches", emptyResult.isEmpty());
            
        } catch (Exception e) {
            Assert.fail("Test 3 failed with exception: " + e.getMessage());
        }
    }
}