"""
Error handling and edge case tests.
Tests that the system fails gracefully and provides helpful error messages.
"""

import pytest
import os
from pathlib import Path
from unittest.mock import patch, MagicMock


@pytest.mark.unit
def test_invalid_youtube_url_error():
    """Test that invalid YouTube URLs produce helpful errors."""
    from ytqgen.cli import validate_youtube_url
    
    with pytest.raises(ValueError) as exc_info:
        validate_youtube_url("https://vimeo.com/123456")
    
    assert "Only YouTube URLs are supported" in str(exc_info.value)
    assert "youtube.com" in str(exc_info.value) or "YouTube" in str(exc_info.value)


@pytest.mark.unit
def test_missing_provider_api_key_error():
    """Test that missing API keys produce helpful errors."""
    from ytqgen.cli import validate_api_keys
    
    # Clear all keys
    os.environ.pop('OPENAI_API_KEY', None)
    os.environ.pop('MISTRAL_API_KEY', None)
    os.environ.pop('GROQ_API_KEY', None)
    
    os.environ['LLM_PROVIDER'] = 'groq'
    os.environ['TRANSCRIPTION_PROVIDER'] = 'groq'
    
    with pytest.raises(ValueError) as exc_info:
        validate_api_keys()
    
    error_msg = str(exc_info.value)
    assert "API key missing" in error_msg
    assert "groq" in error_msg.lower()
    assert "GROQ_API_KEY" in error_msg


@pytest.mark.unit
def test_invalid_provider_name_error():
    """Test that invalid provider names produce helpful errors."""
    from ytqgen.cli import validate_config
    
    config = {
        "provider": "invalid_llm_provider"
    }
    
    with pytest.raises(ValueError) as exc_info:
        validate_config(config)
    
    error_msg = str(exc_info.value)
    assert "Invalid provider" in error_msg
    assert "openai" in error_msg
    assert "mistral" in error_msg
    assert "groq" in error_msg


@pytest.mark.unit
def test_invalid_percentage_sum_error():
    """Test that invalid percentage sums produce helpful errors."""
    from ytqgen.cli import validate_config
    
    config = {
        "generation": {
            "question_types": {
                "mcq_percentage": 30,
                "subjective_percentage": 80  # Sum = 110, not 100
            }
        }
    }
    
    with pytest.raises(ValueError) as exc_info:
        validate_config(config)
    
    error_msg = str(exc_info.value)
    assert "must sum to 100" in error_msg
    assert "30" in error_msg
    assert "80" in error_msg


@pytest.mark.unit
def test_invalid_model_name_warning(capsys):
    """Test that invalid model names produce warnings."""
    from ytqgen.cli import validate_config
    
    config = {
        "provider": "groq",
        "models": {
            "groq": "invalid-model-name-xyz"
        }
    }
    
    # Should not raise, just warn
    validate_config(config)
    
    captured = capsys.readouterr()
    assert "Warning" in captured.out or "warning" in captured.out.lower()


@pytest.mark.unit
def test_yaml_file_not_found():
    """Test that missing YAML config file is handled."""
    from ytqgen.cli import load_config
    
    config = load_config("nonexistent_config.yml")
    
    # Should return empty dict, not crash
    assert config == {}


@pytest.mark.unit
def test_malformed_yaml_file(temp_output_dir):
    """Test that malformed YAML is handled gracefully."""
    from ytqgen.cli import load_config
    
    bad_yaml = temp_output_dir / "bad.yml"
    bad_yaml.write_text("{ invalid yaml content ][[ not valid")
    
    config = load_config(str(bad_yaml))
    
    # Should return empty dict on error
    assert config == {}


@pytest.mark.unit
def test_empty_yaml_file(temp_output_dir):
    """Test that empty YAML file is handled."""
    from ytqgen.cli import load_config
    
    empty_yaml = temp_output_dir / "empty.yml"
    empty_yaml.write_text("")
    
    config = load_config(str(empty_yaml))
    
    # Should return empty dict
    assert config == {}


@pytest.mark.unit
def test_cli_missing_input_error(capsys):
    """Test that missing --input produces helpful error."""
    from ytqgen.cli import cmd_process
    from argparse import Namespace
    
    args = Namespace(
        input=None,
        output=None,
        config=None,
        provider=None,
        transcription_provider=None,
        openai_key=None,
        mistral_key=None,
        groq_key=None,
        num_questions=None,
        mcq_percentage=None,
        subjective_percentage=None
    )
    
    exit_code = cmd_process(args)
    
    assert exit_code != 0
    captured = capsys.readouterr()
    assert "--input is required" in captured.out or "input" in captured.out.lower()


@pytest.mark.unit
def test_model_loader_unavailable_provider():
    """Test error when provider SDK not installed."""
    from ytqgen.model_loader import get_llm_client
    
    os.environ['LLM_PROVIDER'] = 'groq'
    os.environ['GROQ_API_KEY'] = 'test-key'
    
    with patch('ytqgen.model_loader.GROQ_AVAILABLE', False):
        with pytest.raises(ValueError) as exc_info:
            get_llm_client()
        
        error_msg = str(exc_info.value)
        assert "Groq provider not available" in error_msg
        assert "pip install" in error_msg


@pytest.mark.unit
def test_model_loader_unsupported_provider(clean_env):
    """Test error for unsupported provider."""
    from ytqgen.model_loader import get_llm_client
    
    os.environ['LLM_PROVIDER'] = 'anthropic'  # Not supported
    # Don't set any API keys - it should check provider first
    
    with pytest.raises(ValueError) as exc_info:
        get_llm_client()
    
    error_msg = str(exc_info.value)
    # Should fail with unsupported provider OR missing key (both are valid failures)
    assert "Unsupported LLM provider" in error_msg or "API_KEY is not set" in error_msg


@pytest.mark.unit
def test_negative_num_questions():
    """Test that negative num_questions is caught."""
    from ytqgen.cli import validate_config
    
    config = {
        "generation": {
            "num_questions": -5
        }
    }
    
    with pytest.raises(ValueError) as exc_info:
        validate_config(config)
    
    assert "positive" in str(exc_info.value).lower()


@pytest.mark.unit
def test_zero_num_questions():
    """Test that zero num_questions is caught."""
    from ytqgen.cli import validate_config
    
    config = {
        "generation": {
            "num_questions": 0
        }
    }
    
    with pytest.raises(ValueError) as exc_info:
        validate_config(config)
    
    assert "positive" in str(exc_info.value).lower()


@pytest.mark.unit
def test_non_dict_config():
    """Test that non-dict config is handled gracefully."""
    from ytqgen.cli import apply_config
    
    # Should handle None gracefully (or raise TypeError)
    # Either behavior is acceptable - we just don't want a crash
    try:
        apply_config(None)
    except TypeError:
        # This is acceptable - config should be a dict
        pass
    
    try:
        apply_config([])
    except TypeError:
        pass
    
    try:
        apply_config("string")
    except TypeError:
        pass
    
    # If we get here without crashing, test passes


@pytest.mark.unit
def test_partial_config():
    """Test that partial config (missing fields) works."""
    from ytqgen.cli import apply_config, validate_config
    
    # Config with only some fields
    config = {
        "provider": "groq"
        # Missing: keys, generation, etc.
    }
    
    # Should not crash
    validate_config(config)
    apply_config(config)
    
    assert os.getenv('LLM_PROVIDER') == 'groq'


@pytest.mark.unit
def test_config_with_extra_fields():
    """Test that extra unknown fields in config are ignored."""
    from ytqgen.cli import validate_config, apply_config
    
    config = {
        "provider": "groq",
        "unknown_field": "should be ignored",
        "random_data": {"nested": "stuff"}
    }
    
    # Should not crash, extra fields ignored
    validate_config(config)
    apply_config(config)


@pytest.mark.unit
def test_invalid_youtube_video_id():
    """Test handling of invalid/deleted YouTube video."""
    from ytqgen.audio_processor import download_youtube_audio
    
    # This video ID should not exist
    fake_url = "https://www.youtube.com/watch?v=INVALIDID123456789"
    
    with pytest.raises(Exception):  # Should raise some error
        download_youtube_audio(fake_url)


@pytest.mark.unit
def test_api_call_with_invalid_key(clean_env):
    """Test that invalid API key produces clear error."""
    from ytqgen.model_loader import get_llm_client
    
    os.environ['LLM_PROVIDER'] = 'groq'
    os.environ['GROQ_API_KEY'] = 'invalid_key_123'  # Set the key
    os.environ['YTQGEN_GROQ_MODEL'] = 'llama-3.3-70b-versatile'
    
    with patch('ytqgen.model_loader.GROQ_AVAILABLE', True):
        with patch('ytqgen.model_loader.Groq', create=True) as mock_groq:
            # Simulate API error
            mock_client = MagicMock()
            mock_client.chat.completions.create.side_effect = Exception("Invalid API key")
            mock_groq.return_value = mock_client
            
            client, model, provider = get_llm_client()
            
            with pytest.raises(Exception, match="Invalid API key"):
                client.chat.completions.create(
                    messages=[{"role": "user", "content": "test"}],
                    model=model
                )
