"""Tests for cde_onboard.scaffolder — file scaffolding engine."""

from __future__ import annotations

from pathlib import Path

import pytest

from cde_onboard.constants import PRACTICE_DIR_NAME
from cde_onboard.models import ScaffoldFile
from cde_onboard.scaffolder import scaffold_lesson


class TestScaffoldLesson:
    """Tests for the scaffold_lesson function."""

    def test_creates_single_file(
        self, sample_scaffold_file: ScaffoldFile, practice_dir: Path
    ) -> None:
        """scaffold_lesson creates a single file and returns its path."""
        created = scaffold_lesson(
            lesson_number=3,
            lesson_slug="tasks",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        assert len(created) == 1
        assert created[0].exists()

    def test_creates_correct_directory_structure(
        self, sample_scaffold_file: ScaffoldFile, practice_dir: Path
    ) -> None:
        """Files are placed under <base_dir>/cde-onboard-practice/lesson-NN-<slug>/."""
        scaffold_lesson(
            lesson_number=3,
            lesson_slug="tasks",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        expected_dir = practice_dir / PRACTICE_DIR_NAME / "lesson-03-tasks"
        assert expected_dir.is_dir()

    def test_creates_nested_directories(
        self, sample_scaffold_file: ScaffoldFile, practice_dir: Path
    ) -> None:
        """Relative paths with directories (e.g. .ona/automations.yaml) are created."""
        created = scaffold_lesson(
            lesson_number=3,
            lesson_slug="tasks",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        # .ona/ subdirectory should exist
        ona_dir = created[0].parent
        assert ona_dir.name == ".ona"
        assert ona_dir.is_dir()

    def test_writes_template_content(
        self, sample_scaffold_file: ScaffoldFile, practice_dir: Path
    ) -> None:
        """Scaffolded files contain the actual template content."""
        created = scaffold_lesson(
            lesson_number=3,
            lesson_slug="tasks",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        content = created[0].read_text(encoding="utf-8")
        # The automations_task.yaml template should contain 'tasks:' at minimum
        assert "tasks:" in content
        assert "install-deps:" in content

    def test_creates_multiple_files(
        self, sample_scaffold_files: list[ScaffoldFile], practice_dir: Path
    ) -> None:
        """scaffold_lesson creates all files in the list."""
        created = scaffold_lesson(
            lesson_number=4,
            lesson_slug="services",
            files=sample_scaffold_files,
            base_dir=practice_dir,
        )
        assert len(created) == 2
        for path in created:
            assert path.exists()

    def test_multiple_files_correct_content(
        self, sample_scaffold_files: list[ScaffoldFile], practice_dir: Path
    ) -> None:
        """Each scaffolded file gets the correct template content."""
        created = scaffold_lesson(
            lesson_number=4,
            lesson_slug="services",
            files=sample_scaffold_files,
            base_dir=practice_dir,
        )
        # Find automations.yaml and docker-compose.yaml by name
        automations = [p for p in created if p.name == "automations.yaml"][0]
        compose = [p for p in created if p.name == "docker-compose.yaml"][0]

        automations_content = automations.read_text(encoding="utf-8")
        assert "services:" in automations_content
        assert "commands:" in automations_content

        compose_content = compose.read_text(encoding="utf-8")
        assert "network_mode: host" in compose_content

    def test_empty_scaffold_list(self, practice_dir: Path) -> None:
        """scaffold_lesson with no files returns an empty list and creates nothing."""
        created = scaffold_lesson(
            lesson_number=2,
            lesson_slug="first-environment",
            files=[],
            base_dir=practice_dir,
        )
        assert created == []
        # No practice directory should be created either
        lesson_dir = practice_dir / PRACTICE_DIR_NAME / "lesson-02-first-environment"
        assert not lesson_dir.exists()

    def test_lesson_number_padding(
        self, sample_scaffold_file: ScaffoldFile, practice_dir: Path
    ) -> None:
        """Lesson numbers are zero-padded to two digits."""
        scaffold_lesson(
            lesson_number=1,
            lesson_slug="devcontainer-basics",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        expected_dir = practice_dir / PRACTICE_DIR_NAME / "lesson-01-devcontainer-basics"
        assert expected_dir.is_dir()

    def test_rejects_absolute_paths(self, practice_dir: Path) -> None:
        """Absolute scaffold paths are rejected to prevent traversal."""
        bad_file = ScaffoldFile(
            relative_path="/etc/passwd",
            template_name="automations_task.yaml",
            description="Invalid path",
        )
        with pytest.raises(ValueError):
            scaffold_lesson(
                lesson_number=3,
                lesson_slug="tasks",
                files=[bad_file],
                base_dir=practice_dir,
            )

    def test_rejects_parent_traversal(self, practice_dir: Path) -> None:
        """Parent directory traversal is rejected."""
        bad_file = ScaffoldFile(
            relative_path="../outside.txt",
            template_name="automations_task.yaml",
            description="Invalid path",
        )
        with pytest.raises(ValueError):
            scaffold_lesson(
                lesson_number=3,
                lesson_slug="tasks",
                files=[bad_file],
                base_dir=practice_dir,
            )

    def test_overwrite_existing_file(
        self, sample_scaffold_file: ScaffoldFile, practice_dir: Path
    ) -> None:
        """Scaffolding a second time overwrites existing files without error."""
        scaffold_lesson(
            lesson_number=3,
            lesson_slug="tasks",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        # Scaffold again — should not raise
        created = scaffold_lesson(
            lesson_number=3,
            lesson_slug="tasks",
            files=[sample_scaffold_file],
            base_dir=practice_dir,
        )
        assert len(created) == 1
        assert created[0].exists()

    def test_all_real_lessons_scaffold(self, practice_dir: Path) -> None:
        """Integration test: scaffold files from each real lesson that has them."""
        from cde_onboard.lessons import get_all_lessons

        for cls in get_all_lessons():
            lesson = cls()
            meta = cls.meta()
            files = lesson.scaffold_files()
            if not files:
                continue
            created = scaffold_lesson(
                lesson_number=meta.number,
                lesson_slug=meta.id,
                files=files,
                base_dir=practice_dir,
            )
            for path in created:
                assert path.exists(), f"Expected {path} to exist for lesson {meta.id}"
                content = path.read_text(encoding="utf-8")
                assert len(content) > 0, f"Expected non-empty content in {path}"
