"""Behave steps to cover uncovered branches in context_service."""

import os
import tempfile
from pathlib import Path

from behave import given, then, when

from cleveragents.application.services.context_service import ContextService
from cleveragents.application.services.plan_service import PlanService
from cleveragents.application.services.project_service import ProjectService
from cleveragents.config.settings import Settings
from cleveragents.infrastructure.database.unit_of_work import UnitOfWork
from features.mocks.mock_ai_provider import MockAIProvider


@given("a context service coverage workspace")
def step_context_service_workspace(context):
    temp_dir = Path(tempfile.mkdtemp())
    context.temp_dir = temp_dir
    os.chdir(temp_dir)
    (temp_dir / ".cleveragents").mkdir(exist_ok=True)

    settings = Settings()
    db_path = temp_dir / "test_coverage.db"
    unit_of_work = UnitOfWork(f"sqlite:///{db_path}")
    unit_of_work.init_database()

    project_service = ProjectService(settings, unit_of_work)
    project = project_service.initialize_project(
        name="coverage-project", path=temp_dir, force=True
    )
    plan_service = PlanService(settings, unit_of_work, ai_provider=MockAIProvider())
    plan_service.create_plan(project=project, prompt="coverage plan")

    context.settings = settings
    context.unit_of_work = unit_of_work
    context.project = project
    context.context_service = ContextService(settings, unit_of_work)


@when("I try to add a default ignored file")
def step_add_default_ignored_file(context):
    ignored_dir = context.temp_dir / "__pycache__"
    ignored_dir.mkdir(exist_ok=True)
    ignored_file = ignored_dir / "ignored.pyc"
    ignored_file.write_text("ignored cache")

    context.added_files, context.already_in_context = (
        context.context_service.add_to_context(
            project=context.project, path=ignored_file
        )
    )


@then("the file is skipped without context entries")
def step_verify_default_ignored_skip(context):
    assert context.added_files == []
    assert context.already_in_context == []
    assert context.context_service.list_context(context.project) == []


@given("a .agentsignore file with directory and anchored rules")
def step_create_agentsignore(context):
    ignore_file = context.temp_dir / ".agentsignore"
    ignore_file.write_text("# comment line\nignored_dir/\n/anchored.txt\n")


@when("I check ignore status for matching files")
def step_check_agentsignore_matches(context):
    dir_path = context.temp_dir / "ignored_dir"
    dir_path.mkdir(exist_ok=True)
    dir_file = dir_path / "nested.txt"
    dir_file.write_text("nested")

    anchored_file = context.temp_dir / "anchored.txt"
    anchored_file.write_text("anchored")

    context.dir_ignored = context.context_service._should_ignore(
        context.project, dir_file
    )
    context.anchored_ignored = context.context_service._should_ignore(
        context.project, anchored_file
    )


@then("the directory rule and anchored rule are both applied")
def step_verify_agentsignore_rules(context):
    assert context.dir_ignored is True
    assert context.anchored_ignored is True


@given("project include paths are set to allow only matching files")
def step_set_include_paths(context):
    context.project.settings.include_paths = ["allowed/**"]


@given("project exclude paths block temporary files")
def step_set_exclude_paths(context):
    context.project.settings.exclude_paths = ["*.tmp"]


@when("I check ignore status for included and excluded files")
def step_check_include_exclude(context):
    allowed_dir = context.temp_dir / "allowed"
    allowed_dir.mkdir(exist_ok=True)
    included_file = allowed_dir / "keep.py"
    included_file.write_text("keep")

    excluded_file = context.temp_dir / "notes.tmp"
    excluded_file.write_text("skip")

    outside_include = context.temp_dir / "other" / "skip.py"
    outside_include.parent.mkdir(parents=True, exist_ok=True)
    outside_include.write_text("skip")

    context.included_ignored = context.context_service._should_ignore(
        context.project, included_file
    )
    context.excluded_ignored = context.context_service._should_ignore(
        context.project, excluded_file
    )
    context.outside_include_ignored = context.context_service._should_ignore(
        context.project, outside_include
    )


@then("excluded files are ignored and included files allowed")
def step_verify_include_exclude(context):
    assert context.excluded_ignored is True
    assert context.included_ignored is False


@then("files outside the include globs are ignored")
def step_verify_include_guardrail(context):
    assert context.outside_include_ignored is True


@then("paths outside the project are treated as non matches")
def step_verify_project_path_mismatch(context):
    external_path = context.temp_dir.parent / "external.txt"
    external_path.write_text("outside")
    assert (
        context.context_service._project_path_matches(
            context.project, external_path, "*.txt"
        )
        is False
    )


@when("I run ignore matching against an unrelated base directory")
def step_non_relative_match(context):
    unrelated_base = context.temp_dir / "unrelated"
    unrelated_base.mkdir(exist_ok=True)
    target_file = context.temp_dir / "nested" / "file.txt"
    target_file.parent.mkdir(parents=True, exist_ok=True)
    target_file.write_text("data")

    context.non_relative_match = context.context_service._matches_ignore(
        unrelated_base, "*.txt", target_file
    )


@then("non-relative paths are treated as not ignored")
def step_verify_non_relative(context):
    assert context.non_relative_match is False
