"""Unit tests for sample management functions."""
import unittest
import tempfile
import json
from pathlib import Path

from reaper_mcp.core.util import SAMPLE_DIRS_FILE
from reaper_mcp.audio.samples import (
    list_sample_dirs,
    add_sample_dir,
    remove_sample_dir,
    search_samples,
)


class TestSampleDirectoryManagement(unittest.TestCase):
    """Tests for sample directory management functions."""
    
    def setUp(self):
        """Set up test environment."""
        # Backup existing sample_dirs.json if it exists
        self.backup_file = None
        if SAMPLE_DIRS_FILE.exists():
            self.backup_file = SAMPLE_DIRS_FILE.with_suffix('.json.backup')
            SAMPLE_DIRS_FILE.rename(self.backup_file)
        
        # Create temporary test directory
        self.temp_dir = tempfile.mkdtemp()
    
    def tearDown(self):
        """Clean up test environment."""
        # Restore backup if it exists
        if SAMPLE_DIRS_FILE.exists():
            SAMPLE_DIRS_FILE.unlink()
        
        if self.backup_file and self.backup_file.exists():
            self.backup_file.rename(SAMPLE_DIRS_FILE)
        
        # Clean up temp directory
        if Path(self.temp_dir).exists():
            Path(self.temp_dir).rmdir()
    
    def test_list_sample_dirs_empty(self):
        """Test listing sample directories when none are configured."""
        result = list_sample_dirs.fn()
        
        self.assertIn("sample_dirs", result)
        self.assertEqual(len(result["sample_dirs"]), 0)
    
    def test_add_sample_dir(self):
        """Test adding a sample directory."""
        result = add_sample_dir.fn(self.temp_dir)
        
        self.assertNotIn("error", result)
        self.assertIn("sample_dirs", result)
        self.assertIn(self.temp_dir, result["sample_dirs"])
        
        # Verify it's in the list
        list_result = list_sample_dirs.fn()
        self.assertIn(self.temp_dir, list_result["sample_dirs"])
    
    def test_add_multiple_sample_dirs(self):
        """Test adding multiple sample directories."""
        temp_dir2 = tempfile.mkdtemp()
        
        try:
            add_sample_dir.fn(self.temp_dir)
            add_sample_dir.fn(temp_dir2)
            
            list_result = list_sample_dirs.fn()
            self.assertEqual(len(list_result["sample_dirs"]), 2)
            self.assertIn(self.temp_dir, list_result["sample_dirs"])
            self.assertIn(temp_dir2, list_result["sample_dirs"])
        finally:
            if Path(temp_dir2).exists():
                Path(temp_dir2).rmdir()
    
    def test_add_duplicate_sample_dir(self):
        """Test adding the same directory twice."""
        add_sample_dir.fn(self.temp_dir)
        add_sample_dir.fn(self.temp_dir)
        
        list_result = list_sample_dirs.fn()
        # Should only appear once
        count = list_result["sample_dirs"].count(self.temp_dir)
        self.assertEqual(count, 1)
    
    def test_add_nonexistent_directory(self):
        """Test adding a nonexistent directory (implementation allows this)."""
        result = add_sample_dir.fn("/nonexistent/directory")
        
        # Implementation doesn't validate existence, just adds the path
        self.assertNotIn("error", result)
        self.assertIn("sample_dirs", result)
    
    def test_remove_sample_dir(self):
        """Test removing a sample directory."""
        # First add a directory
        add_sample_dir.fn(self.temp_dir)
        
        # Verify it's there
        list_result = list_sample_dirs.fn()
        self.assertIn(self.temp_dir, list_result["sample_dirs"])
        
        # Remove it
        result = remove_sample_dir.fn(self.temp_dir)
        
        self.assertNotIn("error", result)
        self.assertIn("sample_dirs", result)
        
        # Verify it's gone
        list_result = list_sample_dirs.fn()
        self.assertNotIn(self.temp_dir, list_result["sample_dirs"])
    
    def test_remove_nonexistent_dir(self):
        """Test removing a directory that's not in the list."""
        result = remove_sample_dir.fn("/some/random/path")
        
        self.assertNotIn("error", result)
        self.assertIn("sample_dirs", result)


class TestSearchSamples(unittest.TestCase):
    """Tests for search_samples function."""
    
    def setUp(self):
        """Set up test environment."""
        # Backup existing sample_dirs.json if it exists
        self.backup_file = None
        if SAMPLE_DIRS_FILE.exists():
            self.backup_file = SAMPLE_DIRS_FILE.with_suffix('.json.backup')
            SAMPLE_DIRS_FILE.rename(self.backup_file)
        
        # Create temporary test directory with sample files
        self.temp_dir = tempfile.mkdtemp()
        
        # Create some test files
        self.test_files = [
            "kick.wav",
            "snare.wav",
            "hihat.wav",
            "bass_loop.wav",
            "vocal_sample.mp3",
            "readme.txt",
        ]
        
        for filename in self.test_files:
            filepath = Path(self.temp_dir, filename)
            with open(filepath, 'w') as f:
                f.write("test")
        
        # Add directory to sample dirs
        add_sample_dir.fn(self.temp_dir)
    
    def tearDown(self):
        """Clean up test environment."""
        # Clean up test files
        for filename in self.test_files:
            filepath = Path(self.temp_dir, filename)
            if Path(filepath).exists():
                Path(filepath).unlink()
        
        if Path(self.temp_dir).exists():
            Path(self.temp_dir).rmdir()
        
        # Restore backup
        if SAMPLE_DIRS_FILE.exists():
            SAMPLE_DIRS_FILE.unlink()
        
        if self.backup_file and self.backup_file.exists():
            self.backup_file.rename(SAMPLE_DIRS_FILE)
    
    def test_search_all_samples(self):
        """Test searching for all samples without filters."""
        result = search_samples.fn()
        
        self.assertIn("files", result)
        self.assertGreater(len(result["files"]), 0)
    
    def test_search_with_query(self):
        """Test searching with a query string."""
        result = search_samples.fn(query="kick")
        
        self.assertIn("files", result)
        # Should find kick.wav
        found_kick = any("kick" in s.lower() for s in result["files"])
        self.assertTrue(found_kick)
    
    def test_search_with_extension_filter(self):
        """Test searching with extension filter."""
        result = search_samples.fn(exts=[".wav"])
        
        self.assertIn("files", result)
        # Should only find .wav files
        for sample in result["files"]:
            self.assertTrue(sample.endswith(".wav"))
    
    def test_search_with_multiple_extensions(self):
        """Test searching with multiple extension filters."""
        result = search_samples.fn(exts=[".wav", ".mp3"])
        
        self.assertIn("files", result)
        # Should find both .wav and .mp3 files
        for sample in result["files"]:
            self.assertTrue(sample.endswith(".wav") or sample.endswith(".mp3"))
    
    def test_search_with_limit(self):
        """Test searching with result limit."""
        result = search_samples.fn(limit=2)
        
        self.assertIn("files", result)
        self.assertLessEqual(len(result["files"]), 2)
    
    def test_search_no_sample_dirs(self):
        """Test searching when no sample directories are configured."""
        # Remove the sample directory
        remove_sample_dir.fn(self.temp_dir)
        
        result = search_samples.fn()
        
        # Should return error when no dirs configured
        self.assertIn("error", result)
    
    def test_search_nonexistent_query(self):
        """Test searching for something that doesn't exist."""
        result = search_samples.fn(query="nonexistent_sample_xyz")
        
        self.assertIn("files", result)
        self.assertEqual(len(result["files"]), 0)


class TestSampleDirsPersistence(unittest.TestCase):
    """Tests for sample directories persistence."""
    
    def setUp(self):
        """Set up test environment."""
        # Backup existing sample_dirs.json if it exists
        self.backup_file = None
        if SAMPLE_DIRS_FILE.exists():
            self.backup_file = SAMPLE_DIRS_FILE.with_suffix('.json.backup')
            SAMPLE_DIRS_FILE.rename(self.backup_file)
        
        self.temp_dir = tempfile.mkdtemp()
    
    def tearDown(self):
        """Clean up test environment."""
        # Restore backup
        if SAMPLE_DIRS_FILE.exists():
            SAMPLE_DIRS_FILE.unlink()
        
        if self.backup_file and self.backup_file.exists():
            self.backup_file.rename(SAMPLE_DIRS_FILE)
        
        if Path(self.temp_dir).exists():
            Path(self.temp_dir).rmdir()
    
    def test_sample_dirs_persisted_to_file(self):
        """Test that sample directories are persisted to JSON file."""
        add_sample_dir.fn(self.temp_dir)
        
        # Verify the file exists and contains the directory
        self.assertTrue(SAMPLE_DIRS_FILE.exists())
        
        with open(SAMPLE_DIRS_FILE, 'r') as f:
            data = json.load(f)
        
        self.assertIsInstance(data, list)
        self.assertIn(self.temp_dir, data)
    
    def test_sample_dirs_loaded_from_file(self):
        """Test that sample directories are loaded from JSON file."""
        # Manually create the JSON file
        with open(SAMPLE_DIRS_FILE, 'w') as f:
            json.dump([self.temp_dir], f)
        
        # Now list should return the directory
        result = list_sample_dirs.fn()
        
        self.assertIn(self.temp_dir, result["sample_dirs"])
