import pytest
import shutil
import os
import json
from pathlib import Path
from unittest import mock
from helpers.PlaylistBuilder import PlaylistBuilder

@pytest.fixture
def temp_environment(tmp_path):
    """
    Fixture to set up a temporary environment for testing.
    Creates temporary directories for covers and output,
    copies the sample cover image, and initializes PlaylistBuilder.
    """
    # Define paths within the temporary directory
    covers_dir = tmp_path / "covers"
    history_file = tmp_path / "playlist_history.json"
    output_dir = tmp_path / "output"

    # Create necessary directories
    covers_dir.mkdir(parents=True, exist_ok=True)
    output_dir.mkdir(parents=True, exist_ok=True)

    # Path to the sample cover image in the repository
    sample_cover_src = Path(__file__).parent / 'assets' / 'sample_cover.jpg'

    # Ensure the sample cover exists
    assert sample_cover_src.exists(), f"Sample cover not found at {sample_cover_src}"

    # Copy the sample cover to the covers directory
    shutil.copy(sample_cover_src, covers_dir)

    # Initialize the PlaylistBuilder with the test paths
    builder = PlaylistBuilder(
        covers_dir=str(covers_dir),
        history_file=str(history_file),
        output_dir=str(output_dir)
    )

    # Sample song data
    sample_song = {
        "hash": "7c9e0a7c523395c7ef9d79006b9d42dc6ab8b44a",
        "key": "2a44e",
        "levelId": "custom_level_7c9e0a7c523395c7ef9d79006b9d42dc6ab8b44a",
        "songName": "Gleodream",
        "difficulties": [
            {
                "name": "normal",
                "characteristic": "Standard"
            }
        ]
    }

    yield {
        "builder": builder,
        "covers_dir": covers_dir,
        "history_file": history_file,
        "output_dir": output_dir,
        "sample_song": sample_song,
        "sample_cover_src": sample_cover_src
    }

    # Teardown is handled automatically by pytest's tmp_path fixture

def test_create_playlist(temp_environment, caplog):
    """
    Test the creation of a playlist with a sample song and cover.
    """
    env = temp_environment
    builder = env["builder"]
    covers_dir = env["covers_dir"]
    history_file = env["history_file"]
    output_dir = env["output_dir"]
    sample_song = env["sample_song"]

    # Create the playlist
    playlist_title = "Test Playlist"
    playlist_author = "Test Author"
    playlist_path = builder.create_playlist(
        playlist_data=[sample_song],
        playlist_title=playlist_title,
        playlist_author=playlist_author
    )

    # Verify that the playlist file exists
    assert os.path.exists(playlist_path), "Playlist file was not created."

    # Load the playlist content
    with open(playlist_path, 'r') as f:
        playlist_content = json.load(f)

    # Check playlist metadata
    assert playlist_content['playlistTitle'] == playlist_title
    assert playlist_content['playlistAuthor'] == playlist_author
    assert len(playlist_content['songs']) == 1

    # Verify song details
    song = playlist_content['songs'][0]
    assert song['hash'] == sample_song['hash']
    assert song['key'] == sample_song['key']
    assert song['levelId'] == sample_song['levelId']
    assert song['songName'] == sample_song['songName']
    assert len(song['difficulties']) == 1
    assert song['difficulties'][0]['name'] == 'normal'
    assert song['difficulties'][0]['characteristic'] == 'Standard'

    # Check that an image is encoded and included
    assert 'image' in playlist_content
    assert playlist_content['image'] is not None, "Image was not encoded in the playlist."
    assert playlist_content['image'].startswith("data:image/png;base64,"), "Image encoding is incorrect."

    # Verify coverImage path
    assert 'coverImage' in playlist_content
    expected_cover_path = str(covers_dir / 'sample_cover.jpg')
    assert playlist_content['coverImage'] == expected_cover_path

    # Verify description
    assert 'description' in playlist_content
    assert 'Playlist created by SaberList Tool on' in playlist_content['description']

    # Check that duplicates are not allowed
    assert not playlist_content['allowDuplicates'], "Duplicates should not be allowed."

    # Check customData fields are present but None
    assert 'customData' in playlist_content
    assert playlist_content['customData'] is not None
    assert playlist_content['customData']['syncURL'] is None
    assert playlist_content['customData']['owner'] is None
    assert playlist_content['customData']['id'] is None
    assert playlist_content['customData']['hash'] is None
    assert playlist_content['customData']['shared'] is None

    # Verify that the cover was marked as used in history
    with open(history_file, 'r') as f:
        history = json.load(f)
    assert 'sample_cover.jpg' in history['used_covers'], "Cover image was not marked as used in history."

def test_no_available_covers(temp_environment, caplog):
    """
    Test behavior when no unused cover images are available.
    """
    env = temp_environment
    builder = env["builder"]
    covers_dir = env["covers_dir"]
    history_file = env["history_file"]
    output_dir = env["output_dir"]
    sample_song = env["sample_song"]

    # Remove all covers to simulate no available covers
    shutil.rmtree(covers_dir)
    covers_dir.mkdir(parents=True, exist_ok=True)  # Recreate covers directory without any covers

    # Attempt to create a playlist
    playlist_path = builder.create_playlist(
        playlist_data=[sample_song],
        playlist_title="No Cover Playlist",
        playlist_author="Test Author"
    )

    # Verify that the playlist file exists
    assert os.path.exists(playlist_path), "Playlist file was not created when no covers are available."

    # Load the playlist content
    with open(playlist_path, 'r') as f:
        playlist_content = json.load(f)

    # Check that image and coverImage are None or not set
    assert playlist_content['image'] is None, "Image should be None when no covers are available."
    assert playlist_content['coverImage'] is None, "coverImage should be None when no covers are available."

    # Create another playlist to trigger logging
    builder.create_playlist(
        playlist_data=[sample_song],
        playlist_title="Another No Cover Playlist",
        playlist_author="Test Author"
    )

    # Verify that a warning was logged
    assert any("No unused cover images available. Using no cover." in message for message in caplog.text.split('\n')), \
        "Expected warning was not logged."

def test_history_persistence(temp_environment):
    """
    Test that the history is persisted correctly across multiple playlist creations.
    """
    env = temp_environment
    builder = env["builder"]
    covers_dir = env["covers_dir"]
    history_file = env["history_file"]
    sample_song = env["sample_song"]

    # Create first playlist
    builder.create_playlist(
        playlist_data=[sample_song],
        playlist_title="First Playlist",
        playlist_author="Author A"
    )

    # Attempt to create a second playlist; since there's only one cover, it should warn and not use any cover
    playlist_path = builder.create_playlist(
        playlist_data=[sample_song],
        playlist_title="Second Playlist",
        playlist_author="Author B"
    )

    # Load the second playlist
    with open(playlist_path, 'r') as f:
        playlist_content = json.load(f)

    # Check that no image is set since the cover was already used
    assert playlist_content['image'] is None, "Image should be None when cover is already used."
    assert playlist_content['coverImage'] is None, "coverImage should be None when cover is already used."

    # Verify history contains the cover
    with open(history_file, 'r') as f:
        history = json.load(f)
    assert 'sample_cover.jpg' in history['used_covers'], "Cover image was not marked as used in history."

@mock.patch('saberlist.PlaylistBuilder.random.choice')
def test_random_cover_selection(mock_choice, temp_environment):
    """
    Test that a specific cover is selected when random.choice is mocked.
    """
    env = temp_environment
    builder = env["builder"]
    covers_dir = env["covers_dir"]
    history_file = env["history_file"]
    sample_song = env["sample_song"]
    sample_cover_src = env["sample_cover_src"]

    # Add another cover to the covers directory
    additional_cover = covers_dir / 'additional_cover.jpg'
    shutil.copy(sample_cover_src, additional_cover)

    # Mock random.choice to return 'additional_cover.jpg'
    mock_choice.return_value = 'additional_cover.jpg'

    # Create the playlist
    playlist_path = builder.create_playlist(
        playlist_data=[sample_song],
        playlist_title="Mocked Cover Playlist",
        playlist_author="Mock Author"
    )

    # Verify that the mocked cover was used by checking 'coverImage'
    expected_cover_path = str(covers_dir / 'additional_cover.jpg')
    with open(playlist_path, 'r') as f:
        playlist_content = json.load(f)

    assert playlist_content['coverImage'] == expected_cover_path, "The coverImage should point to 'additional_cover.jpg'."

    # Optionally, verify that 'image' is correctly encoded by checking the data URL prefix
    assert playlist_content['image'].startswith("data:image/png;base64,"), "Image encoding is incorrect."

    # Verify that the history includes the mocked cover
    with open(history_file, 'r') as f:
        history = json.load(f)
    assert 'additional_cover.jpg' in history['used_covers'], "Cover image was not marked as used in history."