"""Tests for scaffold generators (token_manager, admin_dashboard)."""

import ast
from pathlib import Path

import pytest

from stablestack.scaffolds import detect_language, write_scaffold_files
from stablestack.scaffolds import token_manager
from stablestack.scaffolds import admin_dashboard


# =============================================================================
# Language detection
# =============================================================================


class TestDetectLanguage:
    """Tests for detect_language()."""

    def test_python_only(self, tmp_path: Path):
        (tmp_path / "pyproject.toml").write_text("[project]")
        assert detect_language(tmp_path) == "python"

    def test_python_requirements(self, tmp_path: Path):
        (tmp_path / "requirements.txt").write_text("flask")
        assert detect_language(tmp_path) == "python"

    def test_python_setup_py(self, tmp_path: Path):
        (tmp_path / "setup.py").write_text("")
        assert detect_language(tmp_path) == "python"

    def test_python_setup_cfg(self, tmp_path: Path):
        (tmp_path / "setup.cfg").write_text("")
        assert detect_language(tmp_path) == "python"

    def test_typescript_tsconfig(self, tmp_path: Path):
        (tmp_path / "tsconfig.json").write_text("{}")
        assert detect_language(tmp_path) == "typescript"

    def test_typescript_package_json(self, tmp_path: Path):
        (tmp_path / "package.json").write_text("{}")
        assert detect_language(tmp_path) == "typescript"

    def test_both_returns_none(self, tmp_path: Path):
        (tmp_path / "pyproject.toml").write_text("[project]")
        (tmp_path / "package.json").write_text("{}")
        assert detect_language(tmp_path) is None

    def test_neither_returns_none(self, tmp_path: Path):
        assert detect_language(tmp_path) is None


# =============================================================================
# write_scaffold_files
# =============================================================================


class TestWriteScaffoldFiles:
    """Tests for write_scaffold_files()."""

    def test_creates_files(self, tmp_path: Path):
        files = {
            "mydir/__init__.py": "# init",
            "mydir/foo.py": "x = 1",
        }
        created = write_scaffold_files(tmp_path, files)
        assert set(created) == {"mydir/__init__.py", "mydir/foo.py"}
        assert (tmp_path / "mydir" / "__init__.py").read_text() == "# init"
        assert (tmp_path / "mydir" / "foo.py").read_text() == "x = 1"

    def test_skips_existing_without_force(self, tmp_path: Path):
        (tmp_path / "existing.py").write_text("original")
        files = {"existing.py": "overwritten"}
        created = write_scaffold_files(tmp_path, files, force=False)
        assert created == []
        assert (tmp_path / "existing.py").read_text() == "original"

    def test_force_overwrites(self, tmp_path: Path):
        (tmp_path / "existing.py").write_text("original")
        files = {"existing.py": "overwritten"}
        created = write_scaffold_files(tmp_path, files, force=True)
        assert created == ["existing.py"]
        assert (tmp_path / "existing.py").read_text() == "overwritten"

    def test_creates_nested_directories(self, tmp_path: Path):
        files = {"a/b/c/deep.py": "deep = True"}
        created = write_scaffold_files(tmp_path, files)
        assert created == ["a/b/c/deep.py"]
        assert (tmp_path / "a" / "b" / "c" / "deep.py").exists()


# =============================================================================
# Token Manager scaffold
# =============================================================================


class TestTokenManagerScaffold:
    """Tests for token_manager scaffold content."""

    def test_python_files_returned(self):
        files = token_manager.get_files("python")
        expected = {
            "stablestack_token_manager/__init__.py",
            "stablestack_token_manager/encryption.py",
            "stablestack_token_manager/models.py",
            "stablestack_token_manager/rotation.py",
            "stablestack_token_manager/api.py",
            "scripts/rotate_tokens.py",
            "stablestack_token_manager/ui/TokenDashboard.tsx",
        }
        assert set(files.keys()) == expected

    def test_typescript_files_returned(self):
        files = token_manager.get_files("typescript")
        expected = {
            "stablestack_token_manager/index.ts",
            "stablestack_token_manager/encryption.ts",
            "stablestack_token_manager/models.ts",
            "stablestack_token_manager/rotation.ts",
            "stablestack_token_manager/api.ts",
            "scripts/rotate_tokens.ts",
            "stablestack_token_manager/ui/TokenDashboard.tsx",
        }
        assert set(files.keys()) == expected

    def test_python_syntax_valid(self):
        """All Python files should be valid syntax."""
        files = token_manager.get_files("python")
        for path, content in files.items():
            if path.endswith(".py"):
                try:
                    ast.parse(content)
                except SyntaxError as e:
                    pytest.fail(f"Syntax error in {path}: {e}")

    def test_python_content_nonempty(self):
        files = token_manager.get_files("python")
        for path, content in files.items():
            assert len(content.strip()) > 0, f"{path} is empty"

    def test_typescript_content_nonempty(self):
        files = token_manager.get_files("typescript")
        for path, content in files.items():
            assert len(content.strip()) > 0, f"{path} is empty"

    def test_slash_command_nonempty(self):
        cmd = token_manager.get_slash_command()
        assert len(cmd) > 100
        assert "token-manager" in cmd.lower() or "token manager" in cmd.lower()

    def test_python_encryption_has_key_classes(self):
        content = token_manager.get_files("python")["stablestack_token_manager/encryption.py"]
        assert "class TokenEncryptor" in content
        assert "class TokenEncryptorAESGCM" in content
        assert "def encrypt_token" in content
        assert "def decrypt_token" in content

    def test_python_models_has_credential(self):
        content = token_manager.get_files("python")["stablestack_token_manager/models.py"]
        assert "class Credential" in content
        assert "class TokenStatus" in content
        assert "to_safe_dict" in content

    def test_python_rotation_has_provider(self):
        content = token_manager.get_files("python")["stablestack_token_manager/rotation.py"]
        assert "class OAuthProvider" in content
        assert "class GenericOAuthProvider" in content
        assert "class TokenRotationJob" in content
        assert "REFRESH_THRESHOLD_DAYS" in content

    def test_python_api_has_routes(self):
        content = token_manager.get_files("python")["stablestack_token_manager/api.py"]
        assert "router" in content
        assert "list_credentials" in content
        assert "create_credential" in content
        assert "rotate_all" in content

    def test_ts_encryption_has_class(self):
        content = token_manager.get_files("typescript")["stablestack_token_manager/encryption.ts"]
        assert "class TokenEncryptor" in content
        assert "encryptToken" in content
        assert "decryptToken" in content

    def test_ts_models_has_service(self):
        content = token_manager.get_files("typescript")["stablestack_token_manager/models.ts"]
        assert "class CredentialService" in content
        assert "TokenStatus" in content

    def test_ts_rotation_has_provider(self):
        content = token_manager.get_files("typescript")["stablestack_token_manager/rotation.ts"]
        assert "GenericOAuthProvider" in content
        assert "TokenRotationJob" in content
        assert "REFRESH_THRESHOLD_DAYS" in content

    def test_python_cron_script_syntax(self):
        content = token_manager.get_files("python")["scripts/rotate_tokens.py"]
        ast.parse(content)
        assert "TokenRotationJob" in content
        assert "dry-run" in content or "dry_run" in content

    def test_ts_cron_script(self):
        content = token_manager.get_files("typescript")["scripts/rotate_tokens.ts"]
        assert "TokenRotationJob" in content
        assert "dry-run" in content or "dry_run" in content

    def test_react_ui_component(self):
        """React UI is the same for both languages."""
        py_ui = token_manager.get_files("python")["stablestack_token_manager/ui/TokenDashboard.tsx"]
        ts_ui = token_manager.get_files("typescript")["stablestack_token_manager/ui/TokenDashboard.tsx"]
        assert py_ui == ts_ui  # Same React component for both backends
        assert "TokenDashboard" in py_ui
        assert "handleRefresh" in py_ui
        assert "handleRotateAll" in py_ui
        assert "/api/credentials" in py_ui


# =============================================================================
# Admin Dashboard scaffold
# =============================================================================


class TestAdminDashboardScaffold:
    """Tests for admin_dashboard scaffold content."""

    def test_python_files_returned(self):
        files = admin_dashboard.get_files("python")
        expected = {
            "stablestack_admin_dashboard/__init__.py",
            "stablestack_admin_dashboard/monitor.py",
            "stablestack_admin_dashboard/models.py",
            "stablestack_admin_dashboard/jobs.py",
            "stablestack_admin_dashboard/api.py",
            "stablestack_admin_dashboard/ui/AdminDashboard.tsx",
        }
        assert set(files.keys()) == expected

    def test_typescript_files_returned(self):
        files = admin_dashboard.get_files("typescript")
        expected = {
            "stablestack_admin_dashboard/index.ts",
            "stablestack_admin_dashboard/monitor.ts",
            "stablestack_admin_dashboard/types.ts",
            "stablestack_admin_dashboard/jobs.ts",
            "stablestack_admin_dashboard/api.ts",
            "stablestack_admin_dashboard/ui/AdminDashboard.tsx",
        }
        assert set(files.keys()) == expected

    def test_python_syntax_valid(self):
        """All Python files should be valid syntax."""
        files = admin_dashboard.get_files("python")
        for path, content in files.items():
            if path.endswith(".py"):
                try:
                    ast.parse(content)
                except SyntaxError as e:
                    pytest.fail(f"Syntax error in {path}: {e}")

    def test_python_content_nonempty(self):
        files = admin_dashboard.get_files("python")
        for path, content in files.items():
            assert len(content.strip()) > 0, f"{path} is empty"

    def test_typescript_content_nonempty(self):
        files = admin_dashboard.get_files("typescript")
        for path, content in files.items():
            assert len(content.strip()) > 0, f"{path} is empty"

    def test_slash_command_nonempty(self):
        cmd = admin_dashboard.get_slash_command()
        assert len(cmd) > 100
        assert "admin" in cmd.lower()

    def test_python_monitor_has_classes(self):
        content = admin_dashboard.get_files("python")["stablestack_admin_dashboard/monitor.py"]
        assert "class InfrastructureMonitor" in content
        assert "class HealthResult" in content
        assert "def make_db_check" in content
        assert "def make_redis_check" in content
        assert "def make_http_check" in content

    def test_python_models_has_response_types(self):
        content = admin_dashboard.get_files("python")["stablestack_admin_dashboard/models.py"]
        assert "class HealthResponse" in content
        assert "class ActiveQuery" in content
        assert "class DatabaseStats" in content
        assert "class UserSummary" in content

    def test_python_api_has_routes(self):
        content = admin_dashboard.get_files("python")["stablestack_admin_dashboard/api.py"]
        assert "router" in content
        assert "admin_health" in content
        assert "db_stats" in content
        assert "terminate_query" in content
        assert "list_users" in content
        assert "token_status" in content

    def test_python_api_has_token_manager_integration(self):
        content = admin_dashboard.get_files("python")["stablestack_admin_dashboard/api.py"]
        assert "stablestack_token_manager" in content

    def test_ts_monitor_has_class(self):
        content = admin_dashboard.get_files("typescript")["stablestack_admin_dashboard/monitor.ts"]
        assert "class InfrastructureMonitor" in content
        assert "makeDbCheck" in content
        assert "makeRedisCheck" in content

    def test_ts_types_has_interfaces(self):
        content = admin_dashboard.get_files("typescript")["stablestack_admin_dashboard/types.ts"]
        assert "HealthResponse" in content
        assert "ActiveQuery" in content
        assert "DatabaseStats" in content

    def test_ts_api_has_routes(self):
        content = admin_dashboard.get_files("typescript")["stablestack_admin_dashboard/api.ts"]
        assert "adminRouter" in content
        assert "/admin/health" in content
        assert "/admin/db/stats" in content
        assert "/admin/tokens/status" in content

    def test_react_ui_component(self):
        """React UI is the same for both languages."""
        py_ui = admin_dashboard.get_files("python")["stablestack_admin_dashboard/ui/AdminDashboard.tsx"]
        ts_ui = admin_dashboard.get_files("typescript")["stablestack_admin_dashboard/ui/AdminDashboard.tsx"]
        assert py_ui == ts_ui
        assert "AdminDashboard" in py_ui
        assert "Health" in py_ui
        assert "Database" in py_ui
        assert "Active Queries" in py_ui
        assert "/api/admin" in py_ui


# =============================================================================
# End-to-end: write to disk and verify
# =============================================================================


class TestEndToEnd:
    """Test full scaffold generation to disk."""

    @pytest.mark.parametrize("language", ["python", "typescript"])
    def test_token_manager_write(self, tmp_path: Path, language: str):
        files = token_manager.get_files(language)
        created = write_scaffold_files(tmp_path, files)
        assert len(created) == len(files)
        for rel_path in files:
            assert (tmp_path / rel_path).exists()
            assert (tmp_path / rel_path).stat().st_size > 0

    @pytest.mark.parametrize("language", ["python", "typescript"])
    def test_admin_dashboard_write(self, tmp_path: Path, language: str):
        files = admin_dashboard.get_files(language)
        created = write_scaffold_files(tmp_path, files)
        assert len(created) == len(files)
        for rel_path in files:
            assert (tmp_path / rel_path).exists()
            assert (tmp_path / rel_path).stat().st_size > 0

    def test_both_scaffolds_coexist(self, tmp_path: Path):
        """Both scaffolds can be generated in the same project."""
        tm_files = token_manager.get_files("python")
        ad_files = admin_dashboard.get_files("python")

        write_scaffold_files(tmp_path, tm_files)
        write_scaffold_files(tmp_path, ad_files)

        assert (tmp_path / "stablestack_token_manager" / "encryption.py").exists()
        assert (tmp_path / "stablestack_admin_dashboard" / "api.py").exists()
