"""Step definitions for ACMS Index Data Model and File Traversal Engine tests."""

from __future__ import annotations

import tempfile
from datetime import datetime, timedelta
from pathlib import Path

from behave import given, then, when

from cleveragents.acms.index import (
    ACMSIndex,
    FileTraversalEngine,
    FileType,
    IndexEntry,
    TierLevel,
)


@given("I have an ACMS index")
def step_create_index(context):
    """Create a new ACMS index."""
    context.index = ACMSIndex()


@given("I have a file traversal engine with chunk size {chunk_size:d}")
def step_create_traversal_engine(context, chunk_size):
    """Create a file traversal engine with specified chunk size."""
    context.engine = FileTraversalEngine(chunk_size=chunk_size)


@when("I create an index entry with:")
def step_create_index_entry(context):
    """Create an index entry from table data."""
    data = {row["key"]: row["value"] for row in context.table}

    file_type = FileType(data.get("file_type", "other"))
    size_bytes = int(data.get("size_bytes", "0"))

    context.entry = IndexEntry(
        path=data["path"],
        file_type=file_type,
        size_bytes=size_bytes,
        created_at=datetime.now(),
        modified_at=datetime.now(),
    )


@then('the index entry should have path "{path}"')
def step_check_entry_path(context, path):
    """Verify the index entry has the expected path."""
    assert context.entry.path == path


@then('the index entry should have file type "{file_type}"')
def step_check_entry_file_type(context, file_type):
    """Verify the index entry has the expected file type."""
    assert context.entry.file_type == FileType(file_type)


@then("the index entry should have size {size:d} bytes")
def step_check_entry_size(context, size):
    """Verify the index entry has the expected size."""
    assert context.entry.size_bytes == size


@given('I have an index entry with path "{path}"')
def step_create_entry_with_path(context, path):
    """Create an index entry with a specific path."""
    context.entry = IndexEntry(
        path=path,
        file_type=FileType.PYTHON,
        size_bytes=1024,
        created_at=datetime.now(),
        modified_at=datetime.now(),
    )


@when('I add tag "{tag}" to the entry')
def step_add_tag_to_entry(context, tag):
    """Add a tag to the current entry."""
    context.entry.add_tag(tag)


@then('the entry should have tag "{tag}"')
def step_check_entry_has_tag(context, tag):
    """Verify the entry has a specific tag."""
    assert context.entry.has_tag(tag)


@then("the entry should have {count:d} tags")
def step_check_entry_tag_count(context, count):
    """Verify the entry has the expected number of tags."""
    assert len(context.entry.tags) == count


@when('I set the tier level to "{tier}"')
def step_set_entry_tier(context, tier):
    """Set the tier level for the entry."""
    context.entry.set_tier(TierLevel(tier))


@then('the entry should have tier level "{tier}"')
def step_check_entry_tier(context, tier):
    """Verify the entry has the expected tier level."""
    assert context.entry.tier == TierLevel(tier)


@when("I add the entry to the index")
def step_add_entry_to_index(context):
    """Add the current entry to the index."""
    context.index.add_entry(context.entry)


@then("the index should contain {count:d} entry")
def step_check_index_entry_count_singular(context, count):
    """Verify the index has the expected number of entries."""
    assert context.index.get_entry_count() == count


@then("the index should contain {count:d} entries")
def step_check_index_entry_count(context, count):
    """Verify the index has the expected number of entries."""
    assert context.index.get_entry_count() == count


@then('I should be able to retrieve the entry by path "{path}"')
def step_retrieve_entry_by_path(context, path):
    """Verify we can retrieve an entry by path."""
    entry = context.index.get_entry(path)
    assert entry is not None
    assert entry.path == path


@given("I have an index with entries:")
def step_create_index_with_entries(context):
    """Create an index with multiple entries from table data."""
    context.index = ACMSIndex()

    for row in context.table:
        path = row["path"]
        file_type = FileType(row.get("file_type", "other"))

        entry = IndexEntry(
            path=path,
            file_type=file_type,
            size_bytes=1024,
            created_at=datetime.now(),
            modified_at=datetime.now(),
        )

        # Add tags if present
        if "tags" in row:
            for tag in row["tags"].split(","):
                entry.add_tag(tag.strip())

        # Set tier if present
        if "tier" in row:
            entry.set_tier(TierLevel(row["tier"]))

        context.index.add_entry(entry)


@given("I have an index with {count:d} entries")
def step_create_index_with_n_entries(context, count):
    """Create an index with a specified number of entries."""
    context.index = ACMSIndex()
    for i in range(count):
        entry = IndexEntry(
            path=f"/project/file{i}.py",
            file_type=FileType.PYTHON,
            size_bytes=1024,
            created_at=datetime.now(),
            modified_at=datetime.now(),
        )
        context.index.add_entry(entry)


@when('I query the index by path pattern "{pattern}"')
def step_query_by_path_pattern(context, pattern):
    """Query the index by path pattern."""
    context.query_results = context.index.query_by_path(pattern)


@then("I should get {count:d} results")
def step_check_query_result_count(context, count):
    """Verify the query returned the expected number of results."""
    assert len(context.query_results) == count, (
        f"Expected {count} results, got {len(context.query_results)}"
    )


@then("I should get {count:d} result")
def step_check_query_result_count_singular(context, count):
    """Verify the query returned the expected number of results (singular)."""
    assert len(context.query_results) == count, (
        f"Expected {count} result, got {len(context.query_results)}"
    )


@then('the results should include "{path}"')
def step_check_result_includes_path(context, path):
    """Verify the query results include a specific path."""
    paths = [entry.path for entry in context.query_results]
    assert path in paths


@when('I query the index by file type "{file_type}"')
def step_query_by_file_type(context, file_type):
    """Query the index by file type."""
    context.query_results = context.index.query_by_type(FileType(file_type))


@then('all results should have file type "{file_type}"')
def step_check_all_results_file_type(context, file_type):
    """Verify all results have the expected file type."""
    expected_type = FileType(file_type)
    for entry in context.query_results:
        assert entry.file_type == expected_type


@when('I query the index by tag "{tag}"')
def step_query_by_tag(context, tag):
    """Query the index by tag."""
    context.query_results = context.index.query_by_tag(tag)


@when('I query the index by tier level "{tier}"')
def step_query_by_tier(context, tier):
    """Query the index by tier level."""
    context.query_results = context.index.query_by_tier(TierLevel(tier))


@then('the result should have path "{path}"')
def step_check_single_result_path(context, path):
    """Verify the single result has the expected path."""
    assert len(context.query_results) == 1
    assert context.query_results[0].path == path


@given("I have an index with entries from different dates")
def step_create_index_with_dated_entries(context):
    """Create an index with entries from different dates."""
    context.index = ACMSIndex()

    now = datetime.now()
    dates = [
        now - timedelta(days=10),
        now - timedelta(days=5),
        now - timedelta(days=1),
        now,
    ]

    for i, date in enumerate(dates):
        entry = IndexEntry(
            path=f"/project/file{i}.py",
            file_type=FileType.PYTHON,
            size_bytes=1024,
            created_at=date,
            modified_at=date,
        )
        context.index.add_entry(entry)


@when('I query the index for entries modified after "{date_str}"')
def step_query_by_recency(context, date_str):
    """Query the index for entries modified after a specific date."""
    # Parse date string (format: YYYY-MM-DD)
    date = datetime.strptime(date_str, "%Y-%m-%d")
    context.query_results = context.index.query_by_recency(date)


@then("I should get entries modified after that date")
def step_check_recency_results(context):
    """Verify the recency query returned valid results."""
    assert len(context.query_results) > 0


@given("I have a test directory with {count:d} files")
def step_create_test_directory(context, count):
    """Create a temporary directory with test files."""
    context.temp_dir = tempfile.TemporaryDirectory()
    temp_path = Path(context.temp_dir.name)

    # Create subdirectories and files
    for i in range(count):
        subdir = temp_path / f"subdir{i % 10}"
        subdir.mkdir(exist_ok=True)

        file_path = subdir / f"file{i}.py"
        file_path.write_text(f"# Test file {i}\nprint('Hello {i}')\n")


@when("I traverse and index the directory")
def step_traverse_and_index(context):
    """Traverse and index the test directory."""
    context.engine.reset_index()
    context.index = context.engine.traverse_and_index(context.temp_dir.name)


@when("I traverse and index the directory with chunk size {chunk_size:d}")
def step_traverse_and_index_with_chunk_size(context, chunk_size):
    """Traverse and index the test directory with a specific chunk size."""
    engine = FileTraversalEngine(chunk_size=chunk_size)
    context.index = engine.traverse_and_index(context.temp_dir.name)


@then("all entries should have valid file paths")
def step_check_valid_file_paths(context):
    """Verify all entries have valid file paths."""
    for entry in context.index.get_all_entries():
        assert entry.path
        assert len(entry.path) > 0


@then("the traversal should complete without timeout")
def step_check_no_timeout(context):
    """Verify the traversal completed without timeout."""
    # This step passes if we got here without timing out
    assert True


@given("I have a test directory with files including:")
def step_create_test_directory_with_specific_files(context):
    """Create a test directory with specific files."""
    context.temp_dir = tempfile.TemporaryDirectory()
    temp_path = Path(context.temp_dir.name)

    for row in context.table:
        file_path = temp_path / row["path"].lstrip("/")
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text("test content")


@when('I traverse and index the directory excluding "{exclude1}" and "{exclude2}"')
def step_traverse_with_exclusions(context, exclude1, exclude2):
    """Traverse and index with exclusion patterns."""
    context.engine.reset_index()
    context.index = context.engine.traverse_and_index(
        context.temp_dir.name,
        exclude_patterns=[exclude1, exclude2],
    )


@then('the index should not contain "{pattern}" paths')
def step_check_no_excluded_paths(context, pattern):
    """Verify the index doesn't contain paths matching the pattern."""
    for entry in context.index.get_all_entries():
        assert pattern not in entry.path


@when("I get all entries from the index")
def step_get_all_entries(context):
    """Get all entries from the index."""
    context.query_results = context.index.get_all_entries()


@when("I get the entry count")
def step_get_entry_count(context):
    """Get the entry count from the index."""
    context.entry_count = context.index.get_entry_count()


@then("idx the index count should be {count:d}")
def step_check_entry_count_value(context, count):
    """Verify the entry count matches the expected value."""
    assert context.entry_count == count


@when("I remove an entry by path")
def step_remove_entry(context):
    """Remove an entry from the index."""
    # Get the first entry's path
    entries = context.index.get_all_entries()
    if entries:
        context.index.remove_entry(entries[0].path)


@when("I query the index with filters:")
def step_query_with_combined_filters(context):
    """Query the index with multiple filters."""
    filters = {row["filter"]: row["value"] for row in context.table}

    path_pattern = filters.get("path_pattern")
    file_type_str = filters.get("file_type")
    tier_str = filters.get("tier")

    file_type = FileType(file_type_str) if file_type_str else None
    tier = TierLevel(tier_str) if tier_str else None

    context.query_results = context.index.query_combined(
        path_pattern=path_pattern,
        file_type=file_type,
        tier=tier,
    )
