import pytest
from unittest.mock import patch
from datetime import datetime

from clipsai_jp.filesys.json_file import JSONFile
from clipsai_jp.media.audio_file import AudioFile
from clipsai_jp.media.audiovideo_file import AudioVideoFile
from clipsai_jp.media.editor import MediaEditor
from clipsai_jp.media.exceptions import MediaEditorError
from clipsai_jp.transcribe.exceptions import TranscriptionError
from clipsai_jp.transcribe.transcriber import TranscriberConfigManager
from clipsai_jp.transcribe.transcription import Transcription


@pytest.fixture
def transcriber_config_manager():
    return TranscriberConfigManager()


@pytest.fixture
def media_editor():
    return MediaEditor()


@pytest.fixture
def mock_media_editor():
    with patch("clipsai_jp.transcribe.transcriber.MediaEditor") as mock:
        yield mock


# Testing TranscriberConfigManager
def test_assert_valid_config(transcriber_config_manager: TranscriberConfigManager):
    config = {
        "language": "en",
        "precision": "float16",
        "model_size": "medium",
    }
    transcriber_config_manager.assert_valid_config(config)


# Testing MediaEditor
@patch("clipsai_jp.media.temporal_media_file.TemporalMediaFile.assert_exists")
def test_instantiate_as_audio_file(mock_assert_exists, media_editor: MediaEditor):
    mock_assert_exists.return_value = None
    with patch(
        "clipsai_jp.media.temporal_media_file.TemporalMediaFile.has_audio_stream",
        return_value=True,
    ), patch(
        "clipsai_jp.media.temporal_media_file.TemporalMediaFile.has_video_stream",
        return_value=False,
    ):
        result = media_editor.instantiate_as_temporal_media_file("path/to/audio.mp3")
    assert isinstance(result, AudioFile)


@patch("clipsai_jp.media.temporal_media_file.TemporalMediaFile.assert_exists")
def test_instantiate_as_audio_video_file(mock_assert_exists, media_editor: MediaEditor):
    mock_assert_exists.return_value = None
    with patch(
        "clipsai_jp.media.temporal_media_file.TemporalMediaFile.has_audio_stream",
        return_value=True,
    ), patch(
        "clipsai_jp.media.temporal_media_file.TemporalMediaFile.has_video_stream",
        return_value=True,
    ):
        result = media_editor.instantiate_as_temporal_media_file("path/to/video.mp4")
    assert isinstance(result, AudioVideoFile)


@patch("clipsai_jp.media.temporal_media_file.TemporalMediaFile.assert_exists")
def test_instantiate_invalid_file(mock_assert_exists, media_editor: MediaEditor):
    mock_assert_exists.return_value = None
    with patch(
        "clipsai_jp.media.temporal_media_file.TemporalMediaFile.has_audio_stream",
        return_value=False,
    ), patch(
        "clipsai_jp.media.temporal_media_file.TemporalMediaFile.has_video_stream",
        return_value=False,
    ):
        with pytest.raises(MediaEditorError):
            media_editor.instantiate_as_temporal_media_file("path/to/invalid.file")


# Testing Transcription
valid_transcription_data = {
    "source_software": "TestSoftware",
    "time_created": datetime.now(),
    "language": "en",
    "num_speakers": 2,
    "char_info": [
        {"char": "H", "start_time": 0.0, "end_time": 0.2, "speaker": 1},
    ],
}


def test_init_with_valid_dict():
    transcription = Transcription(valid_transcription_data)
    assert transcription.language == "en"


def test_init_with_valid_json_file():
    transcription = Transcription(valid_transcription_data)
    assert isinstance(transcription, Transcription)


def test_init_with_invalid_data():
    with pytest.raises(TypeError):
        Transcription("invalid_data")


def test_get_source_software():
    transcription = Transcription(valid_transcription_data)
    assert transcription.source_software == "TestSoftware"


def test_get_time_spawned():
    transcription = Transcription(valid_transcription_data)
    assert isinstance(transcription.created_time, datetime)


def test_get_char_info_with_time_filter():
    transcription = Transcription(valid_transcription_data)
    char_info = transcription.get_char_info(start_time=0.0, end_time=0.2)
    assert len(char_info) > 0


def test_find_char_index():
    transcription = Transcription(valid_transcription_data)
    index = transcription.find_char_index(0.1, "start")
    assert index >= 0


def test_store_as_json_file():
    mock_json_file = JSONFile("path/to/output.json")
    transcription = Transcription(valid_transcription_data)

    with patch("clipsai_jp.filesys.json_file.JSONFile.assert_has_file_extension"), patch(
        "clipsai_jp.filesys.manager.FileSystemManager.assert_parent_dir_exists"
    ), patch("clipsai_jp.filesys.json_file.JSONFile.delete"), patch(
        "clipsai_jp.filesys.json_file.JSONFile.create", return_value=mock_json_file
    ), patch(
        "clipsai_jp.filesys.json_file.JSONFile.assert_exists"
    ):
        json_file = transcription.store_as_json_file("path/to/output.json")

        json_file_class_name = json_file.__class__.__name__
        mock_json_file_class_name = mock_json_file.__class__.__name__

        assert json_file_class_name == mock_json_file_class_name


def test_invalid_times_exception():
    transcription = Transcription(valid_transcription_data)
    with pytest.raises(TranscriptionError):
        transcription.get_char_info(start_time=-1, end_time=5)
