*** Settings ***
Documentation     Integration tests for database layer with repositories and Unit of Work
Library           OperatingSystem
Library           String
Library           Collections
Library           Process
Library           indentation_library.py
Resource          ${CURDIR}/common.resource

*** Variables ***
${PYTHON}         python
${TEST_PROJECT_NAME}    test-db-project
${TEST_PLAN_NAME}       test-plan
${TEST_FILE}            test_file.py

*** Test Cases ***
Database Can Be Initialized
    [Documentation]    Verify database initialization works
    Create Temporary Project Directory
    Initialize Database
    Database Should Exist
    Cleanup Test Environment

Project Repository CRUD Operations
    [Documentation]    Test Create, Read, Update, Delete operations for projects
    Create Temporary Project Directory
    Initialize Database
    ${project_id}=    Create Project In Database    ${TEST_PROJECT_NAME}
    Should Be True    ${project_id} > 0
    ${project}=    Get Project By ID    ${project_id}
    Should Be Equal    ${project['name']}    ${TEST_PROJECT_NAME}
    Update Project Name    ${project_id}    updated-project
    ${updated}=    Get Project By ID    ${project_id}
    Should Be Equal    ${updated['name']}    updated-project
    Cleanup Test Environment

Plan Repository Operations
    [Documentation]    Test plan repository operations
    Create Temporary Project Directory
    Initialize Database
    ${project_id}=    Create Project In Database    ${TEST_PROJECT_NAME}
    ${plan_id}=    Create Plan For Project    ${project_id}    plan-1
    Should Be True    ${plan_id} > 0
    ${plan2_id}=    Create Plan For Project    ${project_id}    plan-2
    Set Current Plan    ${project_id}    ${plan2_id}
    ${current}=    Get Current Plan For Project    ${project_id}
    Should Be Equal    ${current['name']}    plan-2
    ${all_plans}=    Get All Plans For Project    ${project_id}
    Length Should Be    ${all_plans}    2
    Cleanup Test Environment

Context Repository Operations
    [Documentation]    Test context file management
    Create Temporary Project Directory
    Initialize Database
    ${project_id}=    Create Project In Database    ${TEST_PROJECT_NAME}
    ${plan_id}=    Create Plan For Project    ${project_id}    ${TEST_PLAN_NAME}
    ${context_id}=    Add File To Context    ${plan_id}    /tmp/test.py    100
    Should Be True    ${context_id} > 0
    Add File To Context    ${plan_id}    /tmp/main.py    200
    ${contexts}=    Get Context For Plan    ${plan_id}
    Length Should Be    ${contexts}    2
    Remove Context Item    ${context_id}
    ${remaining}=    Get Context For Plan    ${plan_id}
    Length Should Be    ${remaining}    1
    Clear Context For Plan    ${plan_id}
    ${empty}=    Get Context For Plan    ${plan_id}
    Length Should Be    ${empty}    0
    Cleanup Test Environment

Change Repository Operations
    [Documentation]    Test change tracking
    Create Temporary Project Directory
    Initialize Database
    ${project_id}=    Create Project In Database    ${TEST_PROJECT_NAME}
    ${plan_id}=    Create Plan For Project    ${project_id}    ${TEST_PLAN_NAME}
    ${change_id}=    Add Change To Plan    ${plan_id}    new_file.py    create
    Should Be True    ${change_id} > 0
    Add Change To Plan    ${plan_id}    existing.py    modify
    ${changes}=    Get Changes For Plan    ${plan_id}
    Length Should Be    ${changes}    2
    Mark Change Applied    ${change_id}
    ${updated_changes}=    Get Changes For Plan    ${plan_id}
    ${applied_count}=    Count Applied Changes    ${updated_changes}
    Should Be Equal As Integers    ${applied_count}    1
    Cleanup Test Environment

Unit Of Work Transaction Commit
    [Documentation]    Test that UoW commits all changes together
    Create Temporary Project Directory
    Initialize Database
    Start Transaction
    ${project_id}=    Create Project In Transaction    transaction-project
    ${plan_id}=    Create Plan In Transaction    ${project_id}    transaction-plan
    Commit Transaction
    # Verify both were saved
    ${project}=    Get Project By Name    transaction-project
    Should Not Be Equal    ${project}    ${None}
    ${plans}=    Get All Plans For Project    ${project_id}
    Length Should Be    ${plans}    1
    Cleanup Test Environment

Unit Of Work Transaction Rollback
    [Documentation]    Test that UoW rolls back on error
    Create Temporary Project Directory
    Initialize Database
    Test Transaction Rollback
    # Verify nothing was saved
    ${project}=    Get Project By Name    rollback-project
    Should Be Equal    ${project}    ${None}
    Cleanup Test Environment

Service Layer Uses Repositories
    [Documentation]    Test that services properly use repositories
    Create Temporary Project Directory
    Initialize Project With Service    service-project
    ${project}=    Get Current Project From Service
    Should Not Be Equal    ${project}    ${None}
    Create Plan With Service    Test plan
    ${plan}=    Get Current Plan From Service
    Should Not Be Equal    ${plan}    ${None}
    Create Test File    ${TEST_FILE}    test content
    Add Context With Service    ${TEST_FILE}
    ${context_count}=    Get Context Count From Service
    Should Be True    ${context_count} > 0
    Cleanup Test Environment

End To End Database Workflow
    [Documentation]    Test complete workflow using database
    Create Temporary Project Directory

    # Run the complete workflow in one script
    ${result}=    Run Python Script
    ...    import sys
    ...    import os
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.application.services.context_service import ContextService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    from features.mocks.mock_ai_provider import MockAIProvider
    ...    from pathlib import Path
    ...    os.environ["CLEVERAGENTS_TESTING_USE_MOCK_AI"] = "true"
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    mock_ai = MockAIProvider()
    ...    project_service = ProjectService(settings, uow)
    ...    plan_service = PlanService(settings, uow, mock_ai)
    ...    plan_service.actor_service.ensure_default_mock_actor(force=True)
    ...    context_service = ContextService(settings, uow)
    ...    # Initialize project
    ...    project = project_service.initialize_project('e2e-project', Path('${TEMP_DIR}'))
    ...    # Create plan
    ...    plan = plan_service.create_plan(project, 'Create a REST API')
    ...    # Add context file
    ...    test_file = Path('${TEMP_DIR}/${TEST_FILE}')
    ...    test_file.write_text('# Sample Python file' + chr(10) + 'print("Hello World")')
    ...    context_service.add_to_context(project, test_file)
    ...    # Build plan
    ...    changes = plan_service.build_plan(project)
    ...    # Apply changes
    ...    applied_count = plan_service.apply_changes(project)
    ...    print(applied_count)
    ${applied_count}=    Convert To Integer    ${result}
    Should Be True    ${applied_count} > 0

    Cleanup Test Environment

*** Keywords ***
Create Temporary Project Directory
    [Documentation]    Create a temporary directory for testing
    ${temp_dir}=    Evaluate    tempfile.mkdtemp()    modules=tempfile
    Set Suite Variable    ${TEMP_DIR}    ${temp_dir}
    Set Environment Variable    CLEVERAGENTS_HOME    ${temp_dir}

Initialize Database
    [Documentation]    Initialize the database
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    uow.init_database()
    ...    print('Database initialized')
    Should Contain    ${result}    Database initialized

Database Should Exist
    [Documentation]    Verify database file exists
    File Should Exist    ${TEMP_DIR}/test.db

Create Project In Database
    [Documentation]    Create a project using repository
    [Arguments]    ${name}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Project, ProjectSettings
    ...    from pathlib import Path
    ...    from datetime import datetime
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        project = Project(name='${name}', path=Path('${TEMP_DIR}'), settings=ProjectSettings(), created_at=datetime.now(), updated_at=datetime.now())
    ...        created = ctx.projects.create(project)
    ...        print(created.id)
    ${project_id}=    Convert To Integer    ${result}
    RETURN    ${project_id}

Get Project By ID
    [Documentation]    Get project by ID
    [Arguments]    ${project_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    import json
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        project = ctx.projects.get_by_id(${project_id})
    ...        print(json.dumps({'id': project.id, 'name': project.name}))
    ${project}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${project}

Get Project By Name
    [Documentation]    Get project by name
    [Arguments]    ${name}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    import json
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        project = ctx.projects.get_by_name('${name}')
    ...        if project is None:
    ...            print('null')
    ...        else:
    ...            print(json.dumps({'id': project.id, 'name': project.name}))
    ${project}=    Run Keyword If    '${result}' == 'null'    Set Variable    ${None}
    ...    ELSE    Evaluate    json.loads('''${result}''')    json
    RETURN    ${project}

Update Project Name
    [Documentation]    Update project name
    [Arguments]    ${project_id}    ${new_name}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        project = ctx.projects.get_by_id(${project_id})
    ...        project.name = '${new_name}'
    ...        ctx.projects.update(project)
    ...    print('OK')

Create Plan For Project
    [Documentation]    Create a plan for a project
    [Arguments]    ${project_id}    ${plan_name}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Plan, PlanStatus
    ...    from datetime import datetime
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        plan = Plan(project_id=${project_id}, name='${plan_name}', prompt='Test', status=PlanStatus.PENDING, current=False, created_at=datetime.now(), updated_at=datetime.now())
    ...        created = ctx.plans.create(plan)
    ...        print(created.id)
    ${plan_id}=    Convert To Integer    ${result}
    RETURN    ${plan_id}

Set Current Plan
    [Documentation]    Set a plan as current
    [Arguments]    ${project_id}    ${plan_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        ctx.plans.set_current(${project_id}, ${plan_id})
    ...    print('OK')

Get Current Plan For Project
    [Documentation]    Get current plan for project
    [Arguments]    ${project_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    import json
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        plan = ctx.plans.get_current_for_project(${project_id})
    ...        print(json.dumps({'id': plan.id, 'name': plan.name}) if plan else '{}')
    ${plan}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${plan}

Get All Plans For Project
    [Documentation]    Get all plans for a project
    [Arguments]    ${project_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    import json
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        plans = ctx.plans.get_all_for_project(${project_id})
    ...        print(json.dumps([{'id': p.id, 'name': p.name} for p in plans]))
    ${plans}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${plans}

Add File To Context
    [Documentation]    Add a file to context
    [Arguments]    ${plan_id}    ${file_path}    ${size}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Context, ContextType
    ...    from datetime import datetime
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        context = Context(plan_id=${plan_id}, type=ContextType.FILE, path='${file_path}', content='Test content', file_hash='abc123', size=${size}, added_at=datetime.now())
    ...        created = ctx.contexts.add(context)
    ...        print(created.id)
    ${context_id}=    Convert To Integer    ${result}
    RETURN    ${context_id}

Get Context For Plan
    [Documentation]    Get context for a plan
    [Arguments]    ${plan_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    import json
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        contexts = ctx.contexts.get_for_plan(${plan_id})
    ...        print(json.dumps([{'id': c.id, 'path': c.path} for c in contexts]))
    ${contexts}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${contexts}

Remove Context Item
    [Documentation]    Remove a context item
    [Arguments]    ${context_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        ctx.contexts.remove(${context_id})
    ...    print('OK')

Clear Context For Plan
    [Documentation]    Clear all context for a plan
    [Arguments]    ${plan_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        ctx.contexts.clear_for_plan(${plan_id})
    ...    print('OK')

Add Change To Plan
    [Documentation]    Add a change to a plan
    [Arguments]    ${plan_id}    ${file_path}    ${operation}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Change, OperationType
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        op = OperationType.CREATE if '${operation}' == 'create' else OperationType.MODIFY
    ...        change = Change(plan_id=${plan_id}, file_path='${file_path}', operation=op, new_content='Test content', applied=False)
    ...        created = ctx.changes.add(change)
    ...        print(created.id)
    ${change_id}=    Convert To Integer    ${result}
    RETURN    ${change_id}

Get Changes For Plan
    [Documentation]    Get changes for a plan
    [Arguments]    ${plan_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    import json
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        changes = ctx.changes.get_for_plan(${plan_id})
    ...        print(json.dumps([{'id': c.id, 'applied': c.applied} for c in changes]))
    ${changes}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${changes}

Mark Change Applied
    [Documentation]    Mark a change as applied
    [Arguments]    ${change_id}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        ctx.changes.mark_applied(${change_id})
    ...    print('OK')

Count Applied Changes
    [Documentation]    Count applied changes in a list
    [Arguments]    ${changes}
    ${count}=    Set Variable    0
    FOR    ${change}    IN    @{changes}
        ${applied}=    Get From Dictionary    ${change}    applied
        ${applied_str}=    Convert To String    ${applied}
        IF    '${applied_str}' == 'True'
            ${count}=    Evaluate    ${count} + 1
        END
    END
    RETURN    ${count}

Start Transaction
    [Documentation]    Start a UoW transaction (simulation)
    Set Test Variable    ${TRANSACTION_ACTIVE}    ${True}

Create Project In Transaction
    [Documentation]    Create project in transaction
    [Arguments]    ${name}
    ${error_flag_exists}=    Evaluate    __import__('pathlib').Path('${TEMP_DIR}/error_flag').exists()
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Project, ProjectSettings
    ...    from pathlib import Path
    ...    from datetime import datetime
    ...    error_flag = Path('${TEMP_DIR}/error_flag').exists()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    if error_flag:
    ...        print('0')
    ...    else:
    ...        with uow.transaction() as ctx:
    ...            project = Project(name='${name}', path=Path('${TEMP_DIR}'), settings=ProjectSettings(), created_at=datetime.now(), updated_at=datetime.now())
    ...            created = ctx.projects.create(project)
    ...            print(created.id)
    ${project_id}=    Convert To Integer    ${result}
    RETURN    ${project_id}

Create Plan In Transaction
    [Documentation]    Create plan in transaction
    [Arguments]    ${project_id}    ${name}
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Plan, PlanStatus
    ...    from datetime import datetime
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    with uow.transaction() as ctx:
    ...        plan = Plan(project_id=${project_id}, name='${name}', prompt='.', status=PlanStatus.PENDING, current=True, created_at=datetime.now(), updated_at=datetime.now())
    ...        created = ctx.plans.create(plan)
    ...        print(created.id)
    ${plan_id}=    Convert To Integer    ${result}
    RETURN    ${plan_id}

Commit Transaction
    [Documentation]    Commit the transaction
    Set Test Variable    ${TRANSACTION_ACTIVE}    ${False}

Simulate Transaction Error
    [Documentation]    Simulate an error in transaction
    Set Test Variable    ${TRANSACTION_ERROR}    ${True}
    Create File    ${TEMP_DIR}/error_flag    error

Rollback Transaction
    [Documentation]    Rollback the transaction
    Set Test Variable    ${TRANSACTION_ACTIVE}    ${False}
    # For error simulation, delete the error flag
    Run Python Script
    ...    from pathlib import Path
    ...    error_flag = Path('${TEMP_DIR}/error_flag')
    ...    if error_flag.exists():
    ...        error_flag.unlink()
    # If there was an error simulated, simulate the rollback by querying (in real impl this would rollback DB changes)
    # For now, we'll note that actual transaction rollback isn't supported across multiple keywords

Test Transaction Rollback
    [Documentation]    Test actual transaction rollback with exception
    ${result}=    Run Python Script
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.domain.models.core import Project, ProjectSettings
    ...    from pathlib import Path
    ...    from datetime import datetime
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    try:
    ...        with uow.transaction() as ctx:
    ...            project = Project(name='rollback-project', path=Path('${TEMP_DIR}'), settings=ProjectSettings(), created_at=datetime.now(), updated_at=datetime.now())
    ...            created = ctx.projects.create(project)
    ...            # Force an error to trigger rollback
    ...            raise Exception('Simulated error for rollback test')
    ...    except Exception:
    ...        pass  # Expected exception
    ...    print('Rollback test completed')

Initialize Project With Service
    [Documentation]    Initialize project using service
    [Arguments]    ${name}
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    from pathlib import Path
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    service = ProjectService(settings, uow)
    ...    project = service.initialize_project('${name}', Path('${TEMP_DIR}'))
    ...    print('Project initialized')

Initialize And Get Project With Service
    [Documentation]    Initialize project and return it with ID
    [Arguments]    ${name}
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    from pathlib import Path
    ...    import json
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    service = ProjectService(settings, uow)
    ...    project = service.initialize_project('${name}', Path('${TEMP_DIR}'))
    ...    print(json.dumps({'id': project.id, 'name': project.name, 'path': str(project.path)}))
    ${project}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${project}

Get Current Project From Service
    [Documentation]    Get current project from service
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    service = ProjectService(settings, uow)
    ...    project = service.get_current_project()
    ...    print(project.name if project else 'None')
    RETURN    ${result}

Get Current Project From Service With Debug
    [Documentation]    Get current project from service with debugging info
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import json
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    service = ProjectService(settings, uow)
    ...    project = service.get_current_project()
    ...    if project:
    ...        print(json.dumps({'id': project.id, 'name': project.name, 'path': str(project.path)}))
    ...    else:
    ...        print('null')
    ${project}=    Run Keyword If    '${result}' == 'null'    Set Variable    ${None}
    ...    ELSE    Evaluate    json.loads('''${result}''')    json
    RETURN    ${project}

Create Plan With Service
    [Documentation]    Create plan using service
    [Arguments]    ${prompt}
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    from features.mocks.mock_ai_provider import MockAIProvider
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    mock_ai = MockAIProvider()
    ...    project_service = ProjectService(settings, uow)
    ...    plan_service = PlanService(settings, uow, mock_ai)
    ...    project = project_service.get_current_project()
    ...    plan = plan_service.create_plan(project, '${prompt}')
    ...    print('Plan created')

Create Plan With Service For Project
    [Documentation]    Create plan for a specific project with ID
    [Arguments]    ${project_id}    ${prompt}
    Run Python Script
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    from cleveragents.domain.models import Project
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    plan_service = PlanService(settings, uow)
    ...    # Get the project from database
    ...    with uow.transaction() as ctx:
    ...        project = ctx.projects.get_by_id(${project_id})
    ...    plan = plan_service.create_plan(project, '${prompt}')
    ...    print('Plan created')

Get Current Plan From Service
    [Documentation]    Get current plan from service
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    plan_service = PlanService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    plan = plan_service.get_current_plan(project) if project else None
    ...    print(plan.name if plan else 'None')
    RETURN    ${result}

Create Test File
    [Documentation]    Create a test file
    [Arguments]    ${filename}    ${content}
    Create File    ${TEMP_DIR}/${filename}    ${content}

Add Context With Service
    [Documentation]    Add context using service
    [Arguments]    ${filename}
    Run Python Script
    ...    from cleveragents.application.services.context_service import ContextService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    from pathlib import Path
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    context_service = ContextService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    added = context_service.add_to_context(project, Path('${TEMP_DIR}/${filename}'))
    ...    print(f'Added {len(added)} files')

Get Context Count From Service
    [Documentation]    Get context count from service
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.context_service import ContextService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    context_service = ContextService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    contexts = context_service.list_context(project) if project else []
    ...    print(len(contexts))
    ${count}=    Convert To Integer    ${result}
    RETURN    ${count}

Get Context Size From Service
    [Documentation]    Get context size from service
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.context_service import ContextService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    context_service = ContextService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    size = context_service.get_context_size(project) if project else 0
    ...    print(size)
    ${size}=    Convert To Integer    ${result}
    RETURN    ${size}

Build Plan With Service
    [Documentation]    Build plan using service
    Run Python Script
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    plan_service = PlanService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    changes = plan_service.build_plan(project) if project else []
    ...    print(f'Built {len(changes)} changes')

Get Pending Changes From Service
    [Documentation]    Get pending changes from service
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import json
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    plan_service = PlanService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    changes = plan_service.get_pending_changes(project) if project else []
    ...    print(json.dumps([{'file': c.file_path} for c in changes]))
    ${changes}=    Evaluate    json.loads('''${result}''')    json
    RETURN    ${changes}

Apply Changes With Service
    [Documentation]    Apply changes using service
    ${result}=    Run Python Script
    ...    from cleveragents.application.services.plan_service import PlanService
    ...    from cleveragents.application.services.project_service import ProjectService
    ...    from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
    ...    from cleveragents.config.settings import Settings
    ...    import os
    ...    os.chdir('${TEMP_DIR}')
    ...    settings = Settings()
    ...    uow = UnitOfWork('sqlite:///${TEMP_DIR}/test.db')
    ...    project_service = ProjectService(settings, uow)
    ...    plan_service = PlanService(settings, uow)
    ...    project = project_service.get_current_project()
    ...    count = plan_service.apply_changes(project) if project else 0
    ...    print(count)
    ${count}=    Convert To Integer    ${result}
    RETURN    ${count}

Run Python Script
    [Documentation]    Run a Python script and return output.
    ...    Retries once on transient failure (empty output with non-zero exit)
    ...    to handle CI resource contention when many parallel workers spawn
    ...    Python subprocesses simultaneously.
    [Arguments]    @{lines}
    # Join lines
    ${script}=    Catenate    SEPARATOR=\n    @{lines}
    # Robot Framework strips leading whitespace from continuation lines; reconstruct locally
    ${code}=    Fix Python Indentation    ${script}
    # Prepare logging suppression header (also needs indentation fix)
    ${header_raw}=    Catenate    SEPARATOR=\n
    ...    import sys
    ...    import os
    ...    sys.path.insert(0, '${CURDIR}/..')
    ...    os.environ['PYTHONWARNINGS'] = 'ignore'
    ...    os.environ['CLEVERAGENTS_TESTING'] = 'true'
    ...    # Disable all logging
    ...    import logging
    ...    logging.disable(logging.CRITICAL)
    ...    # Completely disable structlog by replacing it with a no-op logger
    ...    import structlog
    ...    class NoOpLogger:
    ...    def __init__(self, *args, **kwargs): pass
    ...    def debug(self, *args, **kwargs): pass
    ...    def info(self, *args, **kwargs): pass
    ...    def warning(self, *args, **kwargs): pass
    ...    def error(self, *args, **kwargs): pass
    ...    def critical(self, *args, **kwargs): pass
    ...    def bind(self, **kwargs): return self
    ...    def unbind(self, *args): return self
    ...    def try_unbind(self, *args): return self
    ...    def new(self, **kwargs): return self
    ...    import warnings
    ...    warnings.filterwarnings('ignore')
    ...    structlog.get_logger = lambda *args, **kwargs: NoOpLogger()
    # Fix indentation for the header part only
    ${header}=    Fix Python Indentation    ${header_raw}
    # Combine header and code
    ${full_code}=    Catenate    SEPARATOR=\n    ${header}    ${code}
    # Write to a temporary file — use mkstemp so the fd is closed before
    # Create File writes to it (avoids leaking an open descriptor).
    ${temp_file}=    Evaluate    (lambda t: (__import__('os').close(t[0]), t[1])[-1])(__import__('tempfile').mkstemp(suffix='.py', dir='/tmp'))
    Create File    ${temp_file}    ${full_code}
    ${result}=    Run Process    ${PYTHON}    ${temp_file}    timeout=180s    stderr=STDOUT    env:PYTHONWARNINGS=ignore    env:PYTHONDONTWRITEBYTECODE=1
    # Retry once for transient worker pressure (e.g. SIGTERM under heavy CI load)
    IF    ${result.rc} != 0
        Log    First Python script attempt failed with rc=${result.rc}; retrying once. Output: ${result.stdout}    WARN
        ${retry}=    Run Process    ${PYTHON}    ${temp_file}    timeout=180s    stderr=STDOUT    env:PYTHONWARNINGS=ignore    env:PYTHONDONTWRITEBYTECODE=1
        IF    ${retry.rc} == 0
            ${result}=    Set Variable    ${retry}
        ELSE
            Remove File    ${temp_file}
            Fail    Python script execution failed after retry (rc=${retry.rc}): ${retry.stdout}
        END
    END
    Remove File    ${temp_file}
    # Extract just the number from the output (last line)
    ${lines}=    Split String    ${result.stdout}    \n
    ${filtered_lines}=    Create List
    FOR    ${line}    IN    @{lines}
        ${stripped}=    Strip String    ${line}
        # Skip debug/logging lines
        IF    '${stripped}' != '' and not '${stripped}'.startswith('[debug') and not '${stripped}'.startswith('[info') and not '${stripped}'.startswith('[warning')
            Append To List    ${filtered_lines}    ${stripped}
        END
    END
    ${last_line}=    Get From List    ${filtered_lines}    -1
    RETURN    ${last_line}

Cleanup Test Environment
    [Documentation]    Clean up test artifacts
    Remove Directory    ${TEMP_DIR}    recursive=True
    Remove Environment Variable    CLEVERAGENTS_HOME
