import os
from pathlib import Path

SCAFFOLD_CONTENT = ""
INIT_CONTENT = ""


def _prepare_method_name(method_name: str, test_file_name: str) -> str:
    """Convert method name per the chosen naming convention."""
    if test_file_name == "kebabCase":
        return method_name.replace("_", "-")
    # camelCase — keep as-is (Python names are already snake_case)
    return method_name


def _replace_first_segment(rel_path: Path, replacement: str) -> Path:
    """Replace the first path segment with *replacement* (mirrors ts-agent's replaceFirstSegmentOfPath)."""
    parts = rel_path.parts
    if not parts:
        return Path(replacement)
    return Path(replacement, *parts[1:])


def compute_test_file_path(
    source_file_path: str,
    project_path: str,
    method_name: str,
    test_structure: str = "siblingFolder",
    test_file_name: str = "camelCase",
) -> str:
    """
    Compute the per-method test file path, mirroring ts-agent's naming convention.

    siblingFolder:
        {source_dir}/{stem}.early.test/test_{method}_early.py

    rootFolder:
        Replace the first path segment of the source's relative path with 'tests/', then:
        tests/{rest}/{stem}.early.test/test_{method}_early.py
    """
    source_path = Path(source_file_path)
    stem = source_path.stem
    early_suffix = ".early.test"

    folder_name = stem + early_suffix  # e.g. sample.early.test
    file_name = (
        "test_" + _prepare_method_name(method_name, test_file_name) + "_early.py"
    )  # e.g. test_add_early.py

    if test_structure == "siblingFolder":
        return str(source_path.parent / folder_name / file_name)

    # rootFolder: replace first segment of rel path with "tests"
    try:
        rel = source_path.relative_to(project_path)
    except ValueError:
        rel = Path(source_path.name)

    new_rel = _replace_first_segment(rel.parent, "tests")
    tests_dir = Path(project_path) / new_rel
    return str(tests_dir / folder_name / file_name)


def create_scaffold(test_file_path: str, project_path: str | None = None) -> None:
    test_dir = os.path.dirname(os.path.abspath(test_file_path))
    os.makedirs(test_dir, exist_ok=True)

    # Create __init__.py in every intermediate directory between
    # the project root and the test dir so pytest can discover and
    # import the generated tests (important for rootFolder structure
    # where tests/ and its subdirectories need to be packages).
    if project_path:
        abs_project = os.path.abspath(project_path)
        current = test_dir
        while current != abs_project and current.startswith(abs_project + os.sep):
            init_path = os.path.join(current, "__init__.py")
            if not os.path.exists(init_path):
                with open(init_path, "w", encoding="utf-8") as f:
                    f.write(INIT_CONTENT)
            current = os.path.dirname(current)
    else:
        # Fallback: only create __init__.py in the immediate test dir
        init_path = os.path.join(test_dir, "__init__.py")
        if not os.path.exists(init_path):
            with open(init_path, "w", encoding="utf-8") as f:
                f.write(INIT_CONTENT)

    with open(test_file_path, "w", encoding="utf-8") as f:
        f.write(SCAFFOLD_CONTENT)


def create_conftest(
    test_file_path: str,
    project_root: str,
    pythonpath: list[str],
) -> None:
    """Create conftest.py in the test directory with sys.path setup.

    Ensures pythonpath entries are available as absolute paths on sys.path
    before test collection, regardless of how pytest is invoked.
    Skips if a conftest.py already exists (don't overwrite).
    """
    if not pythonpath:
        return

    test_dir = os.path.dirname(os.path.abspath(test_file_path))
    conftest_path = os.path.join(test_dir, "conftest.py")

    if os.path.exists(conftest_path):
        return

    rel_to_root = os.path.relpath(project_root, test_dir)

    lines = [
        "import sys, os",
        "",
        "_project_root = os.path.normpath(",
        f"    os.path.join(os.path.dirname(__file__), {rel_to_root!r})",
        ")",
    ]
    for pp in pythonpath:
        lines.append(f"sys.path.insert(0, os.path.join(_project_root, {pp!r}))")
    lines.append("")

    with open(conftest_path, "w", encoding="utf-8") as f:
        f.write("\n".join(lines))
