
import pytest
from unittest.mock import Mock, call
from project_generator.infrastructure.driven_adapters.code_injector_adapter import CodeInjectorAdapter
from project_generator.domain.models.project_models import ProjectInventory

@pytest.fixture
def mock_utils(mocker):

    mock = mocker.patch('project_generator.infrastructure.driven_adapters.code_injector_adapter.utils')

    mock.to_camel_case.side_effect = lambda s: "".join(word.capitalize() for word in s.split('_'))
    mock.parse_params.return_value = [{"name": "p1", "type": "str"}]
    mock.format_params_for_sig.return_value = "p1: str"
    mock.format_params_for_call.return_value = "p1"

    mock.get_domain_model_template.return_value = "model_content"
    mock.get_domain_gateway_template.return_value = "gateway_content"
    mock.get_domain_error_template.return_value = "error_content"
    mock.get_use_case_template.return_value = "use_case_content"
    mock.get_adapter_template.return_value = "adapter_content"
    mock.get_settings_field_template.return_value = "settings_field_injection"
    mock.get_settings_validator_template.return_value = "settings_validator_injection"
    mock.get_container_import_template.return_value = "container_import_injection"

    mock.get_container_adapter_import_template.return_value = "adapter_import_inj"
    mock.get_container_adapter_template.return_value = "adapter_inj"
    mock.get_container_use_case_template.return_value = "use_case_inj"
    mock.get_adapter_init_import_template.return_value = "adapter_init_imp_inj"
    mock.get_adapter_init_all_template.return_value = "adapter_init_all_inj"
    mock.get_tools_import_template.return_value = "tools_import_inj"
    mock.get_tools_provide_template.return_value = "tools_provide_inj"
    mock.get_tools_bind_template.return_value = "tools_bind_inj"

    mock.get_prompt_use_case_template.return_value = "prompt_uc_content"
    mock.get_prompt_content_template.return_value = "prompt_txt_content"
    mock.get_container_prompt_import_template.return_value = "container_prompt_imp_inj"
    mock.get_container_prompt_use_case_template.return_value = "container_prompt_uc_inj"
    mock.get_prompts_import_template.return_value = "prompts_imp_inj"
    mock.get_prompts_provide_template.return_value = "prompts_provide_inj"
    mock.get_prompts_bind_template.return_value = "prompts_bind_inj"

    mock.get_resource_gateway_template.return_value = "res_gw_content"
    mock.get_resource_use_case_template.return_value = "res_uc_content"
    mock.get_resource_adapter_template.return_value = "res_adapter_content"
    mock.get_container_resource_import_template.return_value = "container_res_imp_inj"
    mock.get_container_resource_adapter_template.return_value = "container_res_adapter_inj"
    mock.get_container_resource_use_case_template.return_value = "container_res_uc_inj"
    mock.get_resources_import_template.return_value = "res_imp_inj"
    mock.get_resources_provide_template.return_value = "res_provide_inj"
    mock.get_resources_bind_template.return_value = "res_bind_inj"


    mock.create_file = Mock()
    mock.add_code_to_file = Mock()
    mocker.patch('os.path.exists', return_value=False)

    return mock

@pytest.fixture
def code_injector():
    return CodeInjectorAdapter()

@pytest.fixture
def inventory():
    return ProjectInventory(is_valid=True, project_path="/fake/project")


def test_inject_tools_calls_utils(code_injector, mock_utils, inventory):
    tools_to_add = [{"name": "my_tool", "description": "d", "params": "p1:str", "return_type": "dict"}]
    code_injector.inject_tools(tools_to_add, inventory)


    mock_utils.create_file.assert_any_call("/fake/project/src/domain/model/my_tool/my_tool_model.py", "model_content")



    mock_utils.add_code_to_file.assert_any_call("/fake/project/src/applications/settings/settings.py", "# ANCHOR_SETTINGS_FIELD (no borrar)", "settings_field_injection")




    assert mock_utils.create_file.call_count == 8 

    assert mock_utils.add_code_to_file.call_count == 12


def test_inject_prompts_calls_utils(code_injector, mock_utils, inventory):
    prompts_to_add = [{"name": "my_prompt", "description": "d"}]
    code_injector.inject_prompts(prompts_to_add, inventory)

    mock_utils.create_file.assert_any_call("/fake/project/src/domain/usecase/get_my_prompt_usecase.py", "prompt_uc_content")
    mock_utils.create_file.assert_any_call("/fake/project/src/infrastructure/driven_adapters/prompts/content/my_prompt.txt", "prompt_txt_content")

    mock_utils.add_code_to_file.assert_any_call("/fake/project/src/applications/settings/container.py", "# ANCHOR_CONTAINER_USE_CASE (no borrar)", "container_prompt_uc_inj")


    assert mock_utils.create_file.call_count == 2
    assert mock_utils.add_code_to_file.call_count == 5


def test_inject_resources_calls_utils(code_injector, mock_utils, inventory):
    resources_to_add = [{"name": "my_resource", "uri": "u", "description": "d", "params": "p1:str", "return_type": "list"}]
    code_injector.inject_resources(resources_to_add, inventory)

    mock_utils.create_file.assert_any_call("/fake/project/src/domain/model/my_resource/gateways/my_resource_gateway.py", "res_gw_content")


    mock_utils.add_code_to_file.assert_any_call("/fake/project/src/applications/settings/container.py", "# ANCHOR_CONTAINER_USE_CASE (no borrar)", "container_res_uc_inj")


    assert mock_utils.create_file.call_count == 5
    assert mock_utils.add_code_to_file.call_count == 6


def test_inject_tools_handles_file_error(code_injector, mock_utils, inventory, capsys):
    mock_utils.create_file.side_effect = OSError("Permission denied")
    tools_to_add = [{"name": "error_tool", "description": "d", "params": "p", "return_type": "t"}]

    code_injector.inject_tools(tools_to_add, inventory)

    captured = capsys.readouterr()
    assert "ERROR al inyectar tool 'error_tool'" in captured.out
    assert "Permission denied" in captured.out

def test_inject_prompts_handles_file_error(code_injector, mock_utils, inventory, capsys):
    mock_utils.create_file.side_effect = OSError("Permission denied")
    prompts_to_add = [{"name": "error_prompt", "description": "d"}]
    code_injector.inject_prompts(prompts_to_add, inventory)
    captured = capsys.readouterr()
    assert "ERROR al inyectar prompt 'error_prompt'" in captured.out
    assert "Permission denied" in captured.out

def test_inject_prompts_handles_missing_name(code_injector, inventory, capsys):
    prompts_to_add = [{"description": "d"}]
    code_injector.inject_prompts(prompts_to_add, inventory)
    captured = capsys.readouterr()
    assert "ERROR al inyectar prompt 'desconocida'" in captured.out
    assert "Nombre de prompt no encontrado" in captured.out

def test_inject_resources_handles_file_error(code_injector, mock_utils, inventory, capsys):
    mock_utils.create_file.side_effect = OSError("Permission denied")
    resources_to_add = [{"name": "error_res", "uri": "u", "description": "d", "params": "p", "return_type": "t"}]
    code_injector.inject_resources(resources_to_add, inventory)
    captured = capsys.readouterr()
    assert "ERROR al inyectar resource 'error_res'" in captured.out
    assert "Permission denied" in captured.out
    
def test_inject_resources_handles_missing_name(code_injector, inventory, capsys):
    resources_to_add = [{"uri": "u", "description": "d"}]
    code_injector.inject_resources(resources_to_add, inventory)
    captured = capsys.readouterr()
    assert "ERROR al inyectar resource 'desconocida'" in captured.out
    assert "Nombre de resource no encontrado" in captured.out
